From 7d4e182c7551ba828febe6b26d4aa0c0fd4fb578 Mon Sep 17 00:00:00 2001 From: Lu Liu <2w6f8c@gmail.com> Date: Sat, 22 Aug 2020 23:58:56 +0800 Subject: [PATCH] Extend Wikidata extension to support arbitrary Wikibase instances (#2810) Closes #1640. All Wikibase-dependent parameters, which were previously hard-coded for Wikidata, are now described in a JSON manifest. The manifest is currently constructed by hand, but, in the future, will hopefully be published by each Wikibase instance at a standard location. * setup the manifest framework * add dependency mechanism to scrutinizers & update tests * add json creators to constraint entities * adapt the backend (units tests are to be updated) * remove the call to prepareDependencies() in the constructor * update code according to review feedback * update scrutinizers tests * fix typo & update ConstraintsV1 * log if a scrutinizer is skipped * update versioning handling in the backend * correct the order of "actual" and "expected" for assertEquals method * use regex to check manifest versions * 1. add wikibase-manager.js, wikibase-dialog.js, etc. 2. move dialog/schema-alignment-dialog.js -> schema-alignment.js 3. remove unused schema-alignment-dialog.html 4. change most mentions of "Wikidata" to "Wikibase" * support saving cookies for different Wikibases & fix LoginCommandTest * fix schema related tests * removed unused WikibaseCredentials * include MediaWiki API endpoint in the schema * fetch language codes for different Wikibases * fix lgtm-bot alerts * keep a connection map (MediaWiki API endpoint => Connection) in ConnectionManager * simplify the constraint configurations of the manifest and remove lots of unnecessary code. * add slash to the end of mediawiki.root * add manifest schema and use ajv to validate the manifest * remove JSONP support (Wikibase manifest host should support CORS) * save manifests on manifest update * add unit tests for Manifest * include the exception in logger.error() method to make it easier to debug * include the message of ManifestException when calling respondError * test multiple connections * test no manifest & test invalid manifest * adapt manage-account-dialog.js to support multiple Wikibase connections * update instance/subclass of related translations * beautify import-schema-dialog.html * use "${lang}" variable in the reconciliation service endpoint of the manifest * adapt schema-alignment.js after introducing "${lang}" variable in the reconciliation service endpoint * use WikibaseManager.getSelectedWikibaseApi() in SchemaAlignment._getPropertyType * replace more mentions of "Wikidata" to "Wikibase" * use WikibaseManager.getSelectedWikibaseApi() in previewrenderer.js * support fetching language codes of different Wikibases in the frontend * skip EditInspector if missing 'property_constraint_pid' in the manifest * improve unit tests for fetching lang codes * skip scrutinizers depending on fetcher if 'property_constraint_pid' is missing in the manifest * make sure the schema alignment panel is set up before rendering * fix preview bug * add getters of "instance of" and "subclass of" to the Manifest interface and use them in NewItemScrutinizer * fix hardcode for Wikidata in WbItemVariable * rename 'entity_prefix' to 'site_iri' and move it from 'manifest.wikibase.properties' to 'manifest.wikibase' * include oauth configurations in the manifest & support logging in with owner-only consumer for Wikibases with the OAuth extension * correct schema fallback logic * select default wikibase according to the saved schema * include maxlag in the manifest * [backend] move maxlag setting from preferences to request parameter * support setting maxlag when uploading edits * rename "Manage Wikibase" to "Select Wikibase instance" and localize it * fix manifest updating bug * include EditGroups in the manifest * add the reconciliation service from the manifest to standard services if it's not present yet when adding a new manifest * update according to review feedback 1. use inherited color variable 2. rename 'gridwroks' to 'openrefine' 3. remove unnecessary 'async: true' 4. add 'format: url' validation to urls to the schema * rename 'wikibasePrefix' to 'siteIri' --- .../wikidata/module/MOD-INF/controller.js | 14 +- .../wikidata/module/images/Wikibase_logo.png | Bin 0 -> 81729 bytes .../wikidata/module/images/close-map.png | Bin 0 -> 1022 bytes .../wikidata/module/langs/translation-bn.json | 96 +-- .../wikidata/module/langs/translation-en.json | 234 +++---- .../module/langs/translation-en_GB.json | 196 +++--- .../wikidata/module/langs/translation-es.json | 200 +++--- .../wikidata/module/langs/translation-fi.json | 176 +++--- .../wikidata/module/langs/translation-fr.json | 166 ++--- .../wikidata/module/langs/translation-it.json | 160 ++--- .../wikidata/module/langs/translation-jp.json | 183 +++--- .../wikidata/module/langs/translation-ko.json | 142 ++--- .../module/langs/translation-nb_NO.json | 190 +++--- .../wikidata/module/langs/translation-nl.json | 136 ++--- .../module/langs/translation-pt_BR.json | 24 +- .../wikidata/module/langs/translation-sv.json | 148 ++--- extensions/wikidata/module/scripts/ajv.min.js | 3 + .../scripts/dialogs/add-wikibase-dialog.html | 21 + .../scripts/dialogs/import-schema-dialog.html | 16 +- .../scripts/dialogs/import-schema-dialog.js | 12 +- .../scripts/dialogs/logged-in-dialog.html | 2 +- .../scripts/dialogs/manage-account-dialog.js | 105 ++-- .../owner-only-consumer-login-dialog.html | 2 +- .../dialogs/password-login-dialog.html | 4 +- .../scripts/dialogs/perform-edits-dialog.html | 34 +- .../scripts/dialogs/perform-edits-dialog.js | 81 ++- .../dialogs/schema-alignment-dialog.html | 49 -- .../scripts/dialogs/wikibase-dialog.html | 18 + .../module/scripts/dialogs/wikibase-dialog.js | 124 ++++ .../wikidata/module/scripts/issues-tab.html | 2 +- .../wikidata/module/scripts/langsuggest.js | 133 ++-- .../module/scripts/menu-bar-extension.js | 237 +++---- .../wikidata/module/scripts/preview-tab.html | 2 +- .../module/scripts/previewrenderer.js | 30 +- ...lignment-dialog.js => schema-alignment.js} | 578 ++++++++++-------- .../module/scripts/warningsrenderer.js | 67 +- .../module/scripts/wikibase-manager.js | 190 ++++++ .../scripts/wikibase-manifest-schema-v1.js | 126 ++++ .../scripts/wikidata-extension-manager.js | 6 + .../module/scripts/wikidata-manifest-v1.0.js | 92 +++ .../styles/dialogs/add-wikibase-dialog.less | 19 + .../styles/dialogs/import-schema-dialog.less | 4 +- .../styles/dialogs/manage-account-dialog.less | 5 - .../module/styles/dialogs/perform-edits.less | 22 + .../styles/dialogs/wikibase-dialog.less | 44 ++ ...nment-dialog.css => schema-alignment.less} | 30 +- extensions/wikidata/pom.xml | 8 +- .../wikidata/commands/ConnectionManager.java | 75 +-- .../wikidata/commands/LoginCommand.java | 164 +++-- .../commands/PerformWikibaseEditsCommand.java | 4 +- .../PreviewWikibaseSchemaCommand.java | 36 +- .../wikidata/editing/EditBatchProcessor.java | 16 +- .../wikidata/editing/WikibaseCredentials.java | 75 --- .../wikidata/manifests/Manifest.java | 23 + .../wikidata/manifests/ManifestException.java | 12 + .../wikidata/manifests/ManifestParser.java | 40 ++ .../wikidata/manifests/ManifestV1.java | 94 +++ .../PerformWikibaseEditsOperation.java | 49 +- .../wikidata/qa/ConstraintFetcher.java | 48 +- .../openrefine/wikidata/qa/EditInspector.java | 41 +- .../qa/WikidataConstraintFetcher.java | 92 --- .../qa/scrutinizers/CalendarScrutinizer.java | 4 + .../CommonDescriptionScrutinizer.java | 4 + .../ConflictsWithScrutinizer.java | 22 +- .../DifferenceWithinRangeScrutinizer.java | 26 +- .../DistinctValuesScrutinizer.java | 10 +- .../qa/scrutinizers/EditScrutinizer.java | 35 +- .../EnglishDescriptionScrutinizer.java | 4 + .../scrutinizers/EntityTypeScrutinizer.java | 21 +- .../qa/scrutinizers/FormatScrutinizer.java | 15 +- .../InverseConstraintScrutinizer.java | 21 +- .../scrutinizers/ItemRequiresScrutinizer.java | 21 +- .../scrutinizers/MultiValueScrutinizer.java | 13 +- .../qa/scrutinizers/NewItemScrutinizer.java | 9 +- .../scrutinizers/NoEditsMadeScrutinizer.java | 7 +- .../QualifierCompatibilityScrutinizer.java | 27 +- .../qa/scrutinizers/QuantityScrutinizer.java | 26 +- .../RestrictedPositionScrutinizer.java | 31 +- .../RestrictedValuesScrutinizer.java | 28 +- .../SelfReferentialScrutinizer.java | 4 + .../scrutinizers/SingleValueScrutinizer.java | 15 +- .../qa/scrutinizers/UnsourcedScrutinizer.java | 9 +- .../UseAsQualifierScrutinizer.java | 20 +- .../scrutinizers/WhitespaceScrutinizer.java | 5 + .../wikidata/schema/ExpressionContext.java | 10 +- .../wikidata/schema/WbItemVariable.java | 2 +- .../wikidata/schema/WbLanguageConstant.java | 10 +- .../wikidata/schema/WbLanguageVariable.java | 3 +- .../wikidata/schema/WikibaseSchema.java | 45 +- .../wikidata/utils/EntityCache.java | 49 +- .../wikidata/utils/LanguageCodeStore.java | 62 +- .../wikidata-monolingualtext-langcode.json | 1 + ...-v1.0-missing-property-constraint-pid.json | 91 +++ ...ata-manifest-v1.0-without-constraints.json | 26 + .../data/manifest/wikidata-manifest-v1.0.json | 92 +++ .../tests/data/operations/perform-edits.json | 1 + .../tests/data/operations/save-schema.json | 4 +- .../data/schema/history_of_medicine.json | 2 +- .../history_of_medicine_normalized.json | 2 +- .../wikidata/tests/data/schema/inception.json | 2 +- .../wikidata/commands/LoginCommandTest.java | 265 +++++--- .../PreviewWikibaseSchemaCommandTest.java | 38 +- .../SaveWikibaseSchemaCommandTest.java | 2 +- .../editing/EditBatchProcessorTest.java | 19 +- .../exporters/SchemaExporterTest.java | 2 +- .../wikidata/manifests/ManifestV1Test.java | 75 +++ .../PerformWikibaseEditsOperationTest.java | 2 +- .../wikidata/qa/ConstraintTest.java | 3 +- .../wikidata/qa/EditInspectorTest.java | 38 ++ .../qa/WikidataConstraintFetcherTests.java | 62 -- .../ConflictsWithScrutinizerTest.java | 7 +- .../DifferenceWithinScrutinizerTest.java | 9 +- .../DistinctValuesScrutinizerTest.java | 3 +- .../EntityTypeScrutinizerTest.java | 11 +- .../scrutinizers/FormatScrutinizerTest.java | 5 +- ... => InverseConstraintScrutinizerTest.java} | 9 +- .../ItemRequiresScrutinizerTest.java | 7 +- .../MultiValueScrutinizerTest.java | 3 +- ...QualifierCompatibilityScrutinizerTest.java | 7 +- .../scrutinizers/QuantityScrutinizerTest.java | 9 +- .../RestrictedPositionScrutinizerTest.java | 9 +- .../RestrictedValuesScrutinizerTest.java | 7 +- .../qa/scrutinizers/ScrutinizerTest.java | 20 +- .../SingleValueScrutinizerTest.java | 3 +- .../UnsourcedScrutinizerTest.java | 3 +- .../UseAsQualifierScrutinizerTest.java | 7 +- .../schema/ExpressionContextTest.java | 6 +- .../wikidata/schema/WbExpressionTest.java | 36 +- .../schema/WbLanguageConstantTest.java | 5 + .../wikidata/schema/WikibaseSchemaTest.java | 2 +- .../wikidata/utils/EntityCacheStub.java | 2 +- 131 files changed, 3962 insertions(+), 2393 deletions(-) create mode 100644 extensions/wikidata/module/images/Wikibase_logo.png create mode 100644 extensions/wikidata/module/images/close-map.png create mode 100644 extensions/wikidata/module/scripts/ajv.min.js create mode 100644 extensions/wikidata/module/scripts/dialogs/add-wikibase-dialog.html delete mode 100644 extensions/wikidata/module/scripts/dialogs/schema-alignment-dialog.html create mode 100644 extensions/wikidata/module/scripts/dialogs/wikibase-dialog.html create mode 100644 extensions/wikidata/module/scripts/dialogs/wikibase-dialog.js rename extensions/wikidata/module/scripts/{dialogs/schema-alignment-dialog.js => schema-alignment.js} (69%) create mode 100644 extensions/wikidata/module/scripts/wikibase-manager.js create mode 100644 extensions/wikidata/module/scripts/wikibase-manifest-schema-v1.js create mode 100644 extensions/wikidata/module/scripts/wikidata-extension-manager.js create mode 100644 extensions/wikidata/module/scripts/wikidata-manifest-v1.0.js create mode 100644 extensions/wikidata/module/styles/dialogs/add-wikibase-dialog.less create mode 100644 extensions/wikidata/module/styles/dialogs/wikibase-dialog.less rename extensions/wikidata/module/styles/{dialogs/schema-alignment-dialog.css => schema-alignment.less} (96%) delete mode 100644 extensions/wikidata/src/org/openrefine/wikidata/editing/WikibaseCredentials.java create mode 100644 extensions/wikidata/src/org/openrefine/wikidata/manifests/Manifest.java create mode 100644 extensions/wikidata/src/org/openrefine/wikidata/manifests/ManifestException.java create mode 100644 extensions/wikidata/src/org/openrefine/wikidata/manifests/ManifestParser.java create mode 100644 extensions/wikidata/src/org/openrefine/wikidata/manifests/ManifestV1.java delete mode 100644 extensions/wikidata/src/org/openrefine/wikidata/qa/WikidataConstraintFetcher.java create mode 100644 extensions/wikidata/tests/data/langcode/wikidata-monolingualtext-langcode.json create mode 100644 extensions/wikidata/tests/data/manifest/wikidata-manifest-v1.0-missing-property-constraint-pid.json create mode 100644 extensions/wikidata/tests/data/manifest/wikidata-manifest-v1.0-without-constraints.json create mode 100644 extensions/wikidata/tests/data/manifest/wikidata-manifest-v1.0.json create mode 100644 extensions/wikidata/tests/src/org/openrefine/wikidata/manifests/ManifestV1Test.java create mode 100644 extensions/wikidata/tests/src/org/openrefine/wikidata/qa/EditInspectorTest.java delete mode 100644 extensions/wikidata/tests/src/org/openrefine/wikidata/qa/WikidataConstraintFetcherTests.java rename extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/{InverseConstaintScrutinizerTest.java => InverseConstraintScrutinizerTest.java} (94%) diff --git a/extensions/wikidata/module/MOD-INF/controller.js b/extensions/wikidata/module/MOD-INF/controller.js index d2f306b0a..59a78e599 100644 --- a/extensions/wikidata/module/MOD-INF/controller.js +++ b/extensions/wikidata/module/MOD-INF/controller.js @@ -1,4 +1,3 @@ - importPackage(org.openrefine.wikidata.commands); /* @@ -56,15 +55,21 @@ function init() { "project/scripts", module, [ + "scripts/ajv.min.js", + "scripts/wikidata-manifest-v1.0.js", + "scripts/wikibase-manifest-schema-v1.js", + "scripts/wikibase-manager.js", "scripts/menu-bar-extension.js", "scripts/warningsrenderer.js", "scripts/langsuggest.js", "scripts/bettersuggest.js", "scripts/previewrenderer.js", - "scripts/dialogs/schema-alignment-dialog.js", + "scripts/schema-alignment.js", + "scripts/wikidata-extension-manager.js", "scripts/dialogs/manage-account-dialog.js", "scripts/dialogs/perform-edits-dialog.js", "scripts/dialogs/import-schema-dialog.js", + "scripts/dialogs/wikibase-dialog.js", "scripts/jquery.uls.data.js", ]); @@ -72,10 +77,13 @@ function init() { "project/styles", module, [ - "styles/dialogs/schema-alignment-dialog.css", + "styles/theme.less", + "styles/schema-alignment.less", "styles/dialogs/manage-account-dialog.less", "styles/dialogs/import-schema-dialog.less", "styles/dialogs/perform-edits.less", + "styles/dialogs/wikibase-dialog.less", + "styles/dialogs/add-wikibase-dialog.less" ]); } diff --git a/extensions/wikidata/module/images/Wikibase_logo.png b/extensions/wikidata/module/images/Wikibase_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..17a3bfc35fb66313b82cf3c3bf8ae31267dfbec0 GIT binary patch literal 81729 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%0DAF42k?$ZsruOf3afdnM6C!m52)Al$h}4j!}Q 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

k>DX1_NTHhT6ee~oCL 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উইকিউপাত্তে প্রবেশ করলে সরাসরি ওপেনরিফাইন থেকে আপনাকে সম্পাদনা আপলোড করতে দেওয়া হবে।", - "wikidata-account/username-label": "ব্যবহারকারী নাম:", - "wikidata-account/password-label": "পাসওয়ার্ড:", - "wikidata-account/close": "বন্ধ করুন", - "wikidata-account/log-in": "প্রবেশ করুন", - "wikidata-account/log-out": "প্রস্থান করুন" + "wikibase-extension/menu-label": "উইকিউপাত্ত", + "wikibase-extension/edit-wikibase-schema": "উইকিউপাত্ত স্কিমা সম্পাদনা করুন", + "wikibase-extension/import-wikibase-schema": "স্কিমা আমদানি করুন", + "wikibase-extension/perform-edits-on-wikibase": "উইকিউপাত্তে সম্পাদনা আপলোড করুন", + "wikibase-extension/export-to-qs": "ক্যুইকস্টেটমেন্টসে রপ্তানি করুন", + "wikibase-extension/export-schema": "স্কিমা রপ্তানি করুন", + "wikibase-extension/export-wikibase-schema": "উইকিউপাত্ত স্কিমা রপ্তানি করুন", + "wikibase-extension/quickstatements-export-name": "ক্যুইকস্টেটমেন্টস", + "wikibase-schema/schema-tab-header": "স্কিমা", + "wikibase-schema/warnings-tab-header": "সমস্যা", + "wikibase-schema/edits-preview-tab-header": "প্রাকদর্শন", + "wikibase-schema/statements-header": "বিবৃতি", + "wikibase-schema/empty-statements": "কোন বিবৃতি যোগ করা হয়নি", + "wikibase-schema/add-item-button": "আইটেম যোগ করুন", + "wikibase-schema/remove": "অপসরণ করুন", + "wikibase-schema/add-statement": "বিবৃতি যোগ করুন", + "wikibase-schema/add-value": "মান যোগ করুন", + "wikibase-schema/add-qualifier": "বাছাই যোগ করুন", + "wikibase-schema/copy-reference": "অনুলিপি করুন", + "wikibase-schema/reference-copied": "অনুলিপি করা হয়েছে", + "wikibase-schema/add-reference": "তথ্যসূত্র যোগ করুন", + "wikibase-schema/add-reference-snak": "যোগ করুন", + "wikibase-schema/property-placeholder": "বৈশিষ্ট্য", + "wikibase-schema/nb-references": " তথ্যসূত্র", + "wikibase-schema/remove-column": "স্তম্ভ অপসরণ করুন", + "wikibase-schema/label": "লেবেল", + "wikibase-schema/description": "বিবরণ", + "wikibase-schema/amount": "পরিমাণ", + "wikibase-schema/unit": "একক", + "wikibase-schema/commons-media": "ফাইলের নাম", + "wikibase-schema/invalid-schema-warning-issues": "আপনার স্কিমা অসম্পূর্ণ, সমস্যাগুলি দেখতে সেটি ঠিক করুন।", + "wikibase-schema/invalid-schema-warning-preview": "আপনার স্কিমা অসম্পূর্ণ, প্রাকদর্শন করতে সেটি ঠিক করুন।", + "wikibase-schema/discard-button": "পরিবর্তন বাতিল করুন", + "wikibase-schema/save-button": "স্কিমা সংরক্ষণ করুন", + "wikibase-schema/close-button": "বন্ধ করুন", + "wikibase-schema/unsaved-changes-alt": "আপনি আপনার উইকিউপাত্ত স্কিমায় অসংরক্ষিত পরিবর্তন করেছেন।", + "wikibase-schema/save-schema-alt": "আপনার স্কিমা ওপেনরিফাইনের সংরক্ষণ করুন। পরিবর্তনগুলি এখনো উইকিউপাত্তে আপলোড করা হয়নি।", + "wikibase-schema/discard-schema-changes-alt": "স্কিমার পরিবর্তনগুলি বাতিল করুন।", + "wikibase-schema/incomplete-schema-could-not-be-saved": "আপনার স্কিমা অসম্পূর্ণ, তাই এখনো সংরক্ষণ করা যাচ্ছে না।", + "wikibase-schema/unsaved-warning": "আপনি আপনার উইকিউপাত্ত স্কিমায় অসংরক্ষিত পরিবর্তন করেছেন। তাও বন্ধ করতে চান?", + "wikibase-preview/new-id": "নতুন আইটেম", + "wikibase-account/dialog-header": "উইকিউপাত্ত অ্যাকাউন্ট", + "wikibase-account/explain-log-in": "উইকিউপাত্তে প্রবেশ করলে সরাসরি ওপেনরিফাইন থেকে আপনাকে সম্পাদনা আপলোড করতে দেওয়া হবে।", + "wikibase-account/username-label": "ব্যবহারকারী নাম:", + "wikibase-account/password-label": "পাসওয়ার্ড:", + "wikibase-account/close": "বন্ধ করুন", + "wikibase-account/log-in": "প্রবেশ করুন", + "wikibase-account/log-out": "প্রস্থান করুন" } diff --git a/extensions/wikidata/module/langs/translation-en.json b/extensions/wikidata/module/langs/translation-en.json index 1a4942851..d94bd0836 100644 --- a/extensions/wikidata/module/langs/translation-en.json +++ b/extensions/wikidata/module/langs/translation-en.json @@ -1,108 +1,126 @@ { - "wikidata-extension/menu-label": "Wikidata", - "wikidata-extension/edit-wikidata-schema": "Edit Wikidata schema", - "wikidata-extension/import-wikidata-schema": "Import schema", - "wikidata-extension/manage-wikidata-account": "Manage Wikidata account", - "wikidata-extension/perform-edits-on-wikidata": "Upload edits to Wikidata", - "wikidata-extension/export-to-qs": "Export to QuickStatements", - "wikidata-extension/qs-file": "QuickStatements file", - "wikidata-extension/wikidata-edits": "Wikidata edits…", - "wikidata-extension/export-schema": "Export schema", - "wikidata-extension/export-wikidata-schema": "Export Wikidata schema", - "wikidata-extension/wikidata-schema": "Wikidata schema", - "wikidata-extension/quickstatements-export-name": "QuickStatements", - "wikidata-schema/dialog-header": "Align to Wikidata", - "wikidata-schema/dialog-explanation": "The Wikidata schema below specifies how your tabular data will be transformed into Wikidata edits. You can drag and drop the column names below in most input boxes: for each row, edits will be generated with the values in these columns.", - "wikidata-schema/preview-explanation": "This tab shows the first edits (out of {nb_edits}) that will be made once you upload the changes to Wikidata. You can use facets to inspect the edits on particular items.", - "wikidata-schema/schema-tab-header": "Schema", - "wikidata-schema/warnings-tab-header": "Issues", - "wikidata-schema/edits-preview-tab-header": "Preview", - "wikidata-schema/statements-header": "Statements", - "wikidata-schema/terms-header": "Terms", - "wikidata-schema/empty-statements": "no statements added", - "wikidata-schema/empty-terms": "no labels, descriptions or aliases added", - "wikidata-schema/add-item-button": "add item", - "wikidata-schema/add-term": "add term", - "wikidata-schema/remove": "remove", - "wikidata-schema/add-statement": "add statement", - "wikidata-schema/add-value": "add value", - "wikidata-schema/add-qualifier": "add qualifier", - "wikidata-schema/copy-reference": "copy", - "wikidata-schema/paste-reference": "paste reference", - "wikidata-schema/reference-copied": "copied", - "wikidata-schema/add-reference": "add reference", - "wikidata-schema/add-reference-snak": "add", - "wikidata-schema/property-placeholder": "property", - "wikidata-schema/nb-references": " references", - "wikidata-schema/remove-column": "remove column", - "wikidata-schema/label": "Label", - "wikidata-schema/label-if-new": "Label (do not override)", - "wikidata-schema/label-override": "Label (override if present)", - "wikidata-schema/description": "Description", - "wikidata-schema/description-if-new": "Description (do not override)", - "wikidata-schema/description-override": "Description (override if present)", - "wikidata-schema/override-term": "override if present", - "wikidata-schema/alias": "Alias", - "wikidata-schema/item-or-reconciled-column": "type item or drag reconciled column here", - "wikidata-schema/amount": "amount", - "wikidata-schema/unit": "unit", - "wikidata-schema/full-url": "full URL including the protocol", - "wikidata-schema/tabular-data-with-prefix": "filename starting with \"Data:\"", - "wikidata-schema/commons-media": "filename", - "wikidata-schema/math-expression": "mathematical expression", - "wikidata-schema/geoshape-with-prefix": "filename starting with \"Data:\"", - "wikidata-schema/datatype-not-supported-yet": "This datatype is not supported yet, sorry.", - "wikidata-schema/invalid-schema-warning-issues": "Your schema is incomplete, fix it to see the issues.", - "wikidata-schema/invalid-schema-warning-preview": "Your schema is incomplete, fix it to see the preview.", - "wikidata-schema/discard-button": "Discard changes", - "wikidata-schema/save-button": "Save schema", - "wikidata-schema/close-button": "Close", - "wikidata-schema/unsaved-changes-alt": "You have made unsaved changes to your Wikidata schema.", - "wikidata-schema/save-schema-alt": "Save the schema to OpenRefine. The changes will not be uploaded to Wikidata yet.", - "wikidata-schema/discard-schema-changes-alt": "Discard the changes made to the schema.", - "wikidata-schema/incomplete-schema-could-not-be-saved": "Your schema is incomplete so it cannot be saved yet.", - "wikidata-schema/unsaved-warning": "You have made unsaved changes to your Wikidata schema. Close anyway?", - "wikidata-preview/new-id": "new item", - "wikidata-account/dialog-header": "Wikidata account", - "wikidata-account/explain-log-in": "Logging in to Wikidata lets you to upload edits directly from OpenRefine.", - "wikidata-account/explain-owner-only-consumer-wiki": "See this wiki to get your owner-only consumer if you don't have one.", - "wikidata-account/explain-password-login": "You can also log in with your username/password.", - "wikidata-account/explain-owner-only-consumer-login": "You can also login with your owner-only consumer.", - "wikidata-account/invalid-credentials": "Invalid credentials", - "wikidata-account/username-label": "Username:", - "wikidata-account/username-placeholder": "username", - "wikidata-account/password-label": "Password:", - "wikidata-account/password-placeholder": "password", - "wikidata-account/consumer-token-label": "Consumer token:", - "wikidata-account/consumer-token-placeholder": "consumer token", - "wikidata-account/consumer-secret-label": "Consumer secret:", - "wikidata-account/consumer-secret-placeholder": "consumer secret", - "wikidata-account/access-token-label": "Access token:", - "wikidata-account/access-token-placeholder": "access token", - "wikidata-account/access-secret-label": "Access secret:", - "wikidata-account/access-secret-placeholder": "access secret", - "wikidata-account/remember-me": "Remember me", - "wikidata-account/password-remember-me-title": "Your password won't be stored. Don't check this if your computer is public.", - "wikidata-account/owner-only-consumer-remember-me-title": "Consumer credentials are stored unencrypted in cookies. Don't check this if your computer is public.", - "wikidata-account/close": "Close", - "wikidata-account/log-in": "Log in", - "wikidata-account/logged-in-as": "You are logged in as:", - "wikidata-account/log-out": "Log out", - "wikidata-account/connecting-to-wikidata": "Connecting to Wikidata…", - "perform-wikidata-edits/dialog-header": "Upload edits to Wikidata", - "perform-wikidata-edits/review-your-edits": "You are about to upload {nb_edits} edits to Wikidata. Please check them carefully. Large edit batches should be submitted for bot review first.", - "perform-wikidata-edits/logged-in-as": "You are logged in as", - "perform-wikidata-edits/edit-summary-label": "Edit summary:", - "perform-wikidata-edits/edit-summary-placeholder": "a few words to describe your edits", - "perform-wikidata-edits/perform-edits": "Upload edits", - "perform-wikidata-edits/cancel": "Cancel", - "perform-wikidata-edits/analyzing-edits": "Analyzing your edits…", - "import-wikibase-schema/dialog-header": "Import Wikidata schema", + "wikibase-extension/menu-label": "Wikidata", + "wikibase-extension/select-wikibase-instance": "Select Wikibase instance", + "wikibase-extension/edit-wikibase-schema": "Edit Wikibase schema", + "wikibase-extension/import-wikibase-schema": "Import schema", + "wikibase-extension/manage-wikibase-account": "Manage Wikibase account", + "wikibase-extension/perform-edits-on-wikibase": "Upload edits to Wikibase", + "wikibase-extension/export-to-qs": "Export to QuickStatements", + "wikibase-extension/qs-file": "QuickStatements file", + "wikibase-extension/wikibase-edits": "Wikibase edits…", + "wikibase-extension/export-schema": "Export schema", + "wikibase-extension/export-wikibase-schema": "Export Wikibase schema", + "wikibase-extension/wikibase-schema": "Wikibase schema", + "wikibase-extension/quickstatements-export-name": "QuickStatements", + "wikibase-management/dialog-header": "Select Wikibase instance", + "wikibase-management/explain-select-wikibase": "Click on an item below to select it as the target Wikibase to work against. This will clear any existing schema. After switching to the target Wikibase, you should reconcile your data against it before editing the schema.", + "wikibase-management/current-selected-wikibase": "Current selected Wikibase: $2", + "wikibase-management/add-wikibase": "Add Wikibase", + "wikibase-management/close": "Close", + "wikibase-management/contact-service": "Contacting manifest service", + "wikibase-management/error-contact": "Error contacting manifest service", + "wikibase-addition/dialog-header": "Add Wikibase manifest", + "wikibase-addition/explain-add-manifest": "The manifest should specify a reconciliation service linked to the Wikibase, the reconciliation service will be added to OpenRefine if not added yet.", + "wikibase-addition/explain-add-manifest-via-url": "Enter the Wikibase manifest's URL (recommended, this is helpful for keeping track of the latest manifest):", + "wikibase-addition/explain-paste-manifest": "Or paste the manifest JSON text directly (manifests for some public Wikibases can be found here, you can also write one yourself according to this tutorial):", + "wikibase-addition/add-wikibase": "Add Wikibase", + "wikibase-addition/cancel": "Cancel", + "wikibase-addition/invalid-manifest": "Invalid Wikibase manifest.", + "wikibase-schema/dialog-header": "Align to Wikibase", + "wikibase-schema/dialog-explanation": "You are currently working against $2. You should have your data reconciled to reconciliation services linked to $2 (such as $3) first. The schema below specifies how your tabular data will be transformed into $2 edits. You can drag and drop the column names below in most input boxes: for each row, edits will be generated with the values in these columns.", + "wikibase-schema/preview-explanation": "This tab shows the first edits (out of $1) that will be made once you upload the changes to $3. You can use facets to inspect the edits on particular items.", + "wikibase-schema/schema-tab-header": "Schema", + "wikibase-schema/warnings-tab-header": "Issues", + "wikibase-schema/edits-preview-tab-header": "Preview", + "wikibase-schema/statements-header": "Statements", + "wikibase-schema/terms-header": "Terms", + "wikibase-schema/empty-statements": "no statements added", + "wikibase-schema/empty-terms": "no labels, descriptions or aliases added", + "wikibase-schema/add-item-button": "add item", + "wikibase-schema/add-term": "add term", + "wikibase-schema/remove": "remove", + "wikibase-schema/add-statement": "add statement", + "wikibase-schema/add-value": "add value", + "wikibase-schema/add-qualifier": "add qualifier", + "wikibase-schema/copy-reference": "copy", + "wikibase-schema/paste-reference": "paste reference", + "wikibase-schema/reference-copied": "copied", + "wikibase-schema/add-reference": "add reference", + "wikibase-schema/add-reference-snak": "add", + "wikibase-schema/property-placeholder": "property", + "wikibase-schema/nb-references": " references", + "wikibase-schema/remove-column": "remove column", + "wikibase-schema/label": "Label", + "wikibase-schema/label-if-new": "Label (do not override)", + "wikibase-schema/label-override": "Label (override if present)", + "wikibase-schema/description": "Description", + "wikibase-schema/description-if-new": "Description (do not override)", + "wikibase-schema/description-override": "Description (override if present)", + "wikibase-schema/override-term": "override if present", + "wikibase-schema/alias": "Alias", + "wikibase-schema/item-or-reconciled-column": "type item or drag reconciled column here", + "wikibase-schema/amount": "amount", + "wikibase-schema/unit": "unit", + "wikibase-schema/full-url": "full URL including the protocol", + "wikibase-schema/tabular-data-with-prefix": "filename starting with \"Data:\"", + "wikibase-schema/commons-media": "filename", + "wikibase-schema/math-expression": "mathematical expression", + "wikibase-schema/geoshape-with-prefix": "filename starting with \"Data:\"", + "wikibase-schema/datatype-not-supported-yet": "This datatype is not supported yet, sorry.", + "wikibase-schema/invalid-schema-warning-issues": "Your schema is incomplete, fix it to see the issues.", + "wikibase-schema/invalid-schema-warning-preview": "Your schema is incomplete, fix it to see the preview.", + "wikibase-schema/discard-button": "Discard changes", + "wikibase-schema/save-button": "Save schema", + "wikibase-schema/close-button": "Close", + "wikibase-schema/unsaved-changes-alt": "You have made unsaved changes to your Wikibase schema.", + "wikibase-schema/save-schema-alt": "Save the schema to OpenRefine. The changes will not be uploaded to Wikibase yet.", + "wikibase-schema/discard-schema-changes-alt": "Discard the changes made to the schema.", + "wikibase-schema/incomplete-schema-could-not-be-saved": "Your schema is incomplete so it cannot be saved yet.", + "wikibase-schema/unsaved-warning": "You have made unsaved changes to your Wikibase schema. Close anyway?", + "wikibase-preview/new-id": "new item", + "wikibase-account/dialog-header": "$1 account", + "wikibase-account/explain-log-in": "Logging in to $2 lets you to upload edits directly from OpenRefine.", + "wikibase-account/explain-owner-only-consumer-wiki": "You need to register an owner-only consumer here first if you don't have one. See this wiki to learn how to get one.", + "wikibase-account/explain-password-login": "You can also login with your username/password.", + "wikibase-account/explain-owner-only-consumer-login": "You can also login with your owner-only consumer.", + "wikibase-account/invalid-credentials": "Invalid credentials", + "wikibase-account/username-label": "Username:", + "wikibase-account/username-placeholder": "username", + "wikibase-account/password-label": "Password:", + "wikibase-account/password-placeholder": "password", + "wikibase-account/consumer-token-label": "Consumer token:", + "wikibase-account/consumer-token-placeholder": "consumer token", + "wikibase-account/consumer-secret-label": "Consumer secret:", + "wikibase-account/consumer-secret-placeholder": "consumer secret", + "wikibase-account/access-token-label": "Access token:", + "wikibase-account/access-token-placeholder": "access token", + "wikibase-account/access-secret-label": "Access secret:", + "wikibase-account/access-secret-placeholder": "access secret", + "wikibase-account/remember-me": "Remember me", + "wikibase-account/password-remember-me-title": "Your password won't be stored. Don't check this if your computer is public.", + "wikibase-account/owner-only-consumer-remember-me-title": "Consumer credentials are stored unencrypted in cookies. Don't check this if your computer is public.", + "wikibase-account/close": "Close", + "wikibase-account/log-in": "Log in", + "wikibase-account/logged-in-as": "You are logged in as:", + "wikibase-account/log-out": "Log out", + "wikibase-account/connecting-to-wikibase": "Connecting to Wikibase…", + "perform-wikibase-edits/dialog-header": "Upload edits to $1", + "perform-wikibase-edits/review-your-edits": "You are about to upload $1 edits to $3. Please check them carefully. Large edit batches should be submitted for bot review first.", + "perform-wikibase-edits/logged-in-as": "You are logged in as", + "perform-wikibase-edits/edit-summary-label": "summary", + "perform-wikibase-edits/edit-summary-placeholder": "a few words to describe your edits", + "perform-wikibase-edits/maxlag-label": "maxlag", + "perform-wikibase-edits/explain-maxlag": "Must be a positive integer. Leave this to the default value unless you know how maxlag works.", + "perform-wikibase-edits/maxlag-validation": "maxlag must be a positive integer", + "perform-wikibase-edits/perform-edits": "Upload edits", + "perform-wikibase-edits/cancel": "Cancel", + "perform-wikibase-edits/analyzing-edits": "Analyzing your edits…", + "import-wikibase-schema/dialog-header": "Import Wikibase schema", "import-wikibase-schema/file-label": "From JSON file: ", "import-wikibase-schema/schema-label": "Or from JSON text:", "import-wikibase-schema/invalid-schema": "Invalid Wikibase schema.", "import-wikibase-schema/import": "Import", - "warnings-messages/new-item-created/title": "This edit batch will create new Wikidata items.", + "warnings-messages/new-item-created/title": "This edit batch will create new {wikibase_name} items.", "warnings-messages/new-item-created/body": "Please make sure that these items do not exist yet and are suitable for inclusion in Wikidata.", "warnings-messages/new-item-without-labels-or-aliases/title": "New items created without any label or alias.", "warnings-messages/new-item-without-labels-or-aliases/body": "You should at least provide one label for new items such as {example_entity}, so that others can understand what the item is about.", @@ -110,12 +128,12 @@ "warnings-messages/new-item-without-descriptions/body": "Adding descriptions on new items such as {example_entity} will make it easier to disambiguate the items from namesakes.", "warnings-messages/new-item-with-deleted-statements/title": "Deleting statements on new items.", "warnings-messages/new-item-with-deleted-statements/body": "There is probably something wrong in your schema or project.", - "warnings-messages/new-item-without-P31-or-P279/title": "New items created without any type.", - "warnings-messages/new-item-without-P31-or-P279/body": "You should provide an \"instance of\" (P31) or \"subclass of\" (P279) statement for each item that you create, such as {example_entity}.", + "warnings-messages/new-item-without-instance-of-or-subclass-of/title": "New items created without any type.", + "warnings-messages/new-item-without-instance-of-or-subclass-of/body": "You should provide an \"instance of\" (P31 for Wikidata) or \"subclass of\" (P279 for Wikidata) statement for each item that you create, such as {example_entity}.", "warnings-messages/add-statements-with-invalid-format/title": "{property_entity} statements with invalid format.", "warnings-messages/add-statements-with-invalid-format/body": "Values for this property are expected to match the regular expression {regex}, which is not the case for {example_value} added on {example_item_entity}.", "warnings-messages/remove-statements-with-invalid-format/title": "Removed statements with invalid format.", - "warnings-messages/remove-statements-with-invalid-format/body": "If these statements currently exist on Wikidata, this will solve constraint violations.", + "warnings-messages/remove-statements-with-invalid-format/body": "If these statements currently exist on {wikibase_name}, this will solve constraint violations.", "warnings-messages/missing-inverse-statements/title": "Inverse statements missing for {added_property_entity}.", "warnings-messages/missing-inverse-statements/body": "Any {added_property_entity} statement such as the one from {source_entity} to {target_entity} should also be added in reverse with {inverse_property_entity}: in this case, {target_entity} {inverse_property_entity} {source_entity}.", "warnings-messages/having-conflicts-with-statements/title": "{property_entity} conflicts with statement {added_property_entity}.", @@ -143,19 +161,19 @@ "warnings-messages/no-edit-generated/title": "No edit was generated.", "warnings-messages/no-edit-generated/body": "There might be something wrong with your schema.", "warnings-messages/no-issue-detected/title": "No issue was detected in your edits.", - "warnings-messages/no-issue-detected/body": "Note that OpenRefine cannot detect all the types of problems Wikidata edits can have.", + "warnings-messages/no-issue-detected/body": "Note that OpenRefine cannot detect all the types of problems Wikibase edits can have.", "warnings-messages/multi-valued-property-is-required-for-new-item/title": "{property_entity} should have more than one statement on new items.", "warnings-messages/multi-valued-property-is-required-for-new-item/body": "This property is expected to have more than one statement on each item but it has single statement, for instance on {example_entity}.", "warnings-messages/multi-valued-property-is-required-for-existing-item/title": "{property_entity} should have more than one statement on existing items.", - "warnings-messages/multi-valued-property-is-required-for-existing-item/body": "This property is expected to have more than one statement on each item but it has single statement, for instance on {example_entity}. If the item already has statements with this property in Wikidata, then this warning can be ignored.", + "warnings-messages/multi-valued-property-is-required-for-existing-item/body": "This property is expected to have more than one statement on each item but it has single statement, for instance on {example_entity}. If the item already has statements with this property in {wikibase_name}, then this warning can be ignored.", "warnings-messages/new-item-requires-property-to-have-certain-values/title": "{property_entity} requires property {added_property_entity} to have certain values.", "warnings-messages/new-item-requires-property-to-have-certain-values/body": "Property {added_property_entity} doesn't have appropriate values such as on item {example_entity}.", "warnings-messages/new-item-requires-certain-other-statement/title": "{property_entity} requires statement with property {added_property_entity}.", "warnings-messages/new-item-requires-certain-other-statement/body": "This property is expected to have another statement with property {added_property_entity} such as on item {example_entity}.", "warnings-messages/existing-item-requires-property-to-have-certain-values/title": "{property_entity} requires property {added_property_entity} to have certain values.", - "warnings-messages/existing-item-requires-property-to-have-certain-values/body": "Property {added_property_entity} doesn't have appropriate values such as on item {example_entity}. If the item already has statements with property {added_property_entity} and with appropriate values in Wikidata, then this warning can be ignored.", + "warnings-messages/existing-item-requires-property-to-have-certain-values/body": "Property {added_property_entity} doesn't have appropriate values such as on item {example_entity}. If the item already has statements with property {added_property_entity} and with appropriate values in {wikibase_name}, then this warning can be ignored.", "warnings-messages/existing-item-requires-certain-other-statement/title": "{property_entity} requires statement with property {added_property_entity}.", - "warnings-messages/existing-item-requires-certain-other-statement/body": "This property is expected to have another statement with property {added_property_entity} such as on item {example_entity}. If the item already has statements with property {added_property_entity} in Wikidata, then this warning can be ignored.", + "warnings-messages/existing-item-requires-certain-other-statement/body": "This property is expected to have another statement with property {added_property_entity} such as on item {example_entity}. If the item already has statements with property {added_property_entity} in {wikibase_name}, then this warning can be ignored.", "warnings-messages/values-should-not-be-used-as-qualifier/title": "Invalid value for qualifier {qualifier_entity} on statement {statement_entity}", "warnings-messages/values-should-not-be-used-as-qualifier/body": "On items such as {example_entity}, an invalid value for qualifier {qualifier_entity} was supplied on statement {statement_entity}.", "warnings-messages/no-references-provided/title": "{property_entity} requires at least one reference.", @@ -171,7 +189,7 @@ "warnings-messages/non-printable-characters/title": "Non-printable characters in strings.", "warnings-messages/non-printable-characters/body": "Strings such as {example_string} contain non-printable characters.", "warnings-messages/invalid-identifier-space/title": "Invalid identifier space for reconciled cells.", - "warnings-messages/invalid-identifier-space/body": "Some reconciled cells such as {example_cell} were ignored because they are not reconciled to Wikidata.", + "warnings-messages/invalid-identifier-space/body": "Some reconciled cells such as {example_cell} were ignored because they are not reconciled to {wikibase_name}.", "warnings-messages/ignored-language/title": "Invalid language identifiers.", "warnings-messages/ignored-language/body": "Some language identifiers are invalid, such as {example_value}. See the allowed values.", "warnings-messages/ignored-date/title": "Invalid date formats.", diff --git a/extensions/wikidata/module/langs/translation-en_GB.json b/extensions/wikidata/module/langs/translation-en_GB.json index 2206aaa95..692a893fc 100644 --- a/extensions/wikidata/module/langs/translation-en_GB.json +++ b/extensions/wikidata/module/langs/translation-en_GB.json @@ -1,5 +1,5 @@ { - "perform-wikidata-edits/analyzing-edits": "Analysing your edits…", + "perform-wikibase-edits/analyzing-edits": "Analysing your edits…", "warnings-messages/item-description-begin-with-article/body": "Description ({lang}) such as {description} on {example_entity} begins with article \"{article}\". Descriptions should not normally begin with initial articles (\"a\", \"an\", \"the\"). See the manual for more information.", "warnings-messages/item-description-begin-with-article/title": "Description begins with article (\"a\", \"an\" or \"the\")", "warnings-messages/item-description-begin-with-uppercase/body": "Description ({lang}) such as {description} on {example_entity} begins with uppercase letter \"{uppercase_letter}\". Descriptions begin with a lowercase letter except when uppercase would normally be required or expected. See the manual for more information.", @@ -72,8 +72,8 @@ "warnings-messages/remove-statements-with-invalid-format/title": "Removed statements with invalid format.", "warnings-messages/add-statements-with-invalid-format/body": "Values for this property are expected to match the regular expression {regex}, which is not the case for {example_value} added on {example_item_entity}.", "warnings-messages/add-statements-with-invalid-format/title": "{property_entity} statements with invalid format.", - "warnings-messages/new-item-without-P31-or-P279/body": "You should provide an \"instance of\" (P31) or \"subclass of\" (P279) statement for each item that you create, such as {example_entity}.", - "warnings-messages/new-item-without-P31-or-P279/title": "New items created without any type.", + "warnings-messages/new-item-without-instance-of-or-subclass-of/body": "You should provide an \"instance of\" (P31) or \"subclass of\" (P279) statement for each item that you create, such as {example_entity}.", + "warnings-messages/new-item-without-instance-of-or-subclass-of/title": "New items created without any type.", "warnings-messages/new-item-with-deleted-statements/body": "There is probably something wrong in your schema or project.", "warnings-messages/new-item-with-deleted-statements/title": "Deleting statements on new items.", "warnings-messages/new-item-without-descriptions/body": "Adding descriptions on new items such as {example_entity} will make it easier to disambiguate the items from namesakes.", @@ -87,99 +87,99 @@ "import-wikibase-schema/schema-label": "Or from JSON text:", "import-wikibase-schema/file-label": "From JSON file: ", "import-wikibase-schema/dialog-header": "Import Wikidata schema", - "perform-wikidata-edits/cancel": "Cancel", - "perform-wikidata-edits/perform-edits": "Upload edits", - "perform-wikidata-edits/edit-summary-placeholder": "a few words to describe your edits", - "perform-wikidata-edits/edit-summary-label": "Edit summary:", - "perform-wikidata-edits/logged-in-as": "You are logged in as", - "perform-wikidata-edits/review-your-edits": "You are about to upload {nb_edits} edits to Wikidata. Please check them carefully. Large edit batches should be submitted for bot review first.", - "perform-wikidata-edits/dialog-header": "Upload edits to Wikidata", - "wikidata-account/connecting-to-wikidata": "Connecting to Wikidata…", - "wikidata-account/dialog-header": "Wikidata account", - "wikidata-account/explain-log-in": "Logging in to Wikidata lets you to upload edits directly from OpenRefine.", - "wikidata-account/explain-owner-only-consumer-wiki": "See this wiki to get your owner-only consumer if you don't have one.", - "wikidata-account/explain-password-login": "You can also login with your username/password.", - "wikidata-account/explain-owner-only-consumer-login": "You can also login with your owner-only consumer.", - "wikidata-account/invalid-credentials": "Invalid credentials", - "wikidata-account/username-label": "Username:", - "wikidata-account/username-placeholder": "username", - "wikidata-account/password-label": "Password:", - "wikidata-account/password-placeholder": "password", - "wikidata-account/consumer-token-label": "Consumer token:", - "wikidata-account/consumer-token-placeholder": "consumer token", - "wikidata-account/consumer-secret-label": "Consumer secret:", - "wikidata-account/consumer-secret-placeholder": "consumer secret", - "wikidata-account/access-token-label": "Access token:", - "wikidata-account/access-token-placeholder": "access token", - "wikidata-account/access-secret-label": "Access secret:", - "wikidata-account/access-secret-placeholder": "access secret", - "wikidata-account/remember-me": "Remember me", - "wikidata-account/password-remember-me-title": "Your password won't be stored. Don't check this if your computer is public.", - "wikidata-account/owner-only-consumer-remember-me-title": "Consumer credentials are stored unencrypted in cookies. Don't check this if your computer is public.", - "wikidata-account/close": "Close", - "wikidata-account/log-in": "Log in", - "wikidata-account/logged-in-as": "You are logged in as:", - "wikidata-account/log-out": "Log out", - "wikidata-preview/new-id": "new item", - "wikidata-schema/unsaved-warning": "You have made unsaved changes to your Wikidata schema. Close anyway?", - "wikidata-schema/incomplete-schema-could-not-be-saved": "Your schema is incomplete so it cannot be saved yet.", - "wikidata-schema/discard-schema-changes-alt": "Discard the changes made to the schema.", - "wikidata-schema/save-schema-alt": "Save the schema to OpenRefine. The changes will not be uploaded to Wikidata yet.", - "wikidata-schema/unsaved-changes-alt": "You have made unsaved changes to your Wikidata schema.", - "wikidata-schema/close-button": "Close", - "wikidata-schema/save-button": "Save schema", - "wikidata-schema/discard-button": "Discard changes", - "wikidata-schema/invalid-schema-warning-preview": "Your schema is incomplete, fix it to see the preview.", - "wikidata-schema/invalid-schema-warning-issues": "Your schema is incomplete, fix it to see the issues.", - "wikidata-schema/datatype-not-supported-yet": "This datatype is not supported yet, sorry.", - "wikidata-schema/geoshape-with-prefix": "filename starting with \"Data:\"", - "wikidata-schema/math-expression": "mathematical expression", - "wikidata-schema/commons-media": "filename", - "wikidata-schema/tabular-data-with-prefix": "filename starting with \"Data:\"", - "wikidata-schema/full-url": "full URL including the protocol", - "wikidata-schema/unit": "unit", - "wikidata-schema/amount": "amount", - "wikidata-schema/item-or-reconciled-column": "type item or drag reconciled column here", - "wikidata-schema/alias": "Alias", - "wikidata-schema/override-term": "override if present", - "wikidata-schema/description-override": "Description (override if present)", - "wikidata-schema/description-if-new": "Description (do not override)", - "wikidata-schema/description": "Description", - "wikidata-schema/label-override": "Label (override if present)", - "wikidata-schema/label-if-new": "Label (do not override)", - "wikidata-schema/label": "Label", - "wikidata-schema/remove-column": "remove column", - "wikidata-schema/nb-references": " references", - "wikidata-schema/property-placeholder": "property", - "wikidata-schema/add-reference-snak": "add", - "wikidata-schema/add-reference": "add reference", - "wikidata-schema/reference-copied": "copied", - "wikidata-schema/paste-reference": "paste reference", - "wikidata-schema/copy-reference": "copy", - "wikidata-schema/add-qualifier": "add qualifier", - "wikidata-schema/add-value": "add value", - "wikidata-schema/add-statement": "add statement", - "wikidata-schema/remove": "remove", - "wikidata-schema/add-term": "add term", - "wikidata-schema/add-item-button": "add item", - "wikidata-schema/empty-terms": "no labels, descriptions or aliases added", - "wikidata-schema/empty-statements": "no statements added", - "wikidata-schema/terms-header": "Terms", - "wikidata-schema/statements-header": "Statements", - "wikidata-schema/edits-preview-tab-header": "Preview", - "wikidata-schema/warnings-tab-header": "Issues", - "wikidata-schema/schema-tab-header": "Schema", - "wikidata-schema/preview-explanation": "This tab shows the first edits (out of {nb_edits}) that will be made once you upload the changes to Wikidata. You can use facets to inspect the edits on particular items.", - "wikidata-schema/dialog-explanation": "The Wikidata schema below specifies how your tabular data will be transformed into Wikidata edits. You can drag and drop the column names below in most input boxes: for each row, edits will be generated with the values in these columns.", - "wikidata-schema/dialog-header": "Align to Wikidata", - "wikidata-extension/quickstatements-export-name": "QuickStatements", - "wikidata-extension/wikidata-schema": "Wikidata schema", - "wikidata-extension/export-wikidata-schema": "Export Wikidata schema", - "wikidata-extension/export-schema": "Export schema", - "wikidata-extension/export-to-qs": "QuickStatements file...", - "wikidata-extension/perform-edits-on-wikidata": "Wikidata edits...", - "wikidata-extension/manage-wikidata-account": "Manage Wikidata account", - "wikidata-extension/import-wikidata-schema": "Import schema", - "wikidata-extension/edit-wikidata-schema": "Edit Wikidata schema", - "wikidata-extension/menu-label": "Wikidata" + "perform-wikibase-edits/cancel": "Cancel", + "perform-wikibase-edits/perform-edits": "Upload edits", + "perform-wikibase-edits/edit-summary-placeholder": "a few words to describe your edits", + "perform-wikibase-edits/edit-summary-label": "Edit summary:", + "perform-wikibase-edits/logged-in-as": "You are logged in as", + "perform-wikibase-edits/review-your-edits": "You are about to upload {nb_edits} edits to Wikidata. Please check them carefully. Large edit batches should be submitted for bot review first.", + "perform-wikibase-edits/dialog-header": "Upload edits to Wikidata", + "wikibase-account/connecting-to-wikibase": "Connecting to Wikidata…", + "wikibase-account/dialog-header": "Wikidata account", + "wikibase-account/explain-log-in": "Logging in to Wikidata lets you to upload edits directly from OpenRefine.", + "wikibase-account/explain-owner-only-consumer-wiki": "See this wiki to get your owner-only consumer if you don't have one.", + "wikibase-account/explain-password-login": "You can also login with your username/password.", + "wikibase-account/explain-owner-only-consumer-login": "You can also login with your owner-only consumer.", + "wikibase-account/invalid-credentials": "Invalid credentials", + "wikibase-account/username-label": "Username:", + "wikibase-account/username-placeholder": "username", + "wikibase-account/password-label": "Password:", + "wikibase-account/password-placeholder": "password", + "wikibase-account/consumer-token-label": "Consumer token:", + "wikibase-account/consumer-token-placeholder": "consumer token", + "wikibase-account/consumer-secret-label": "Consumer secret:", + "wikibase-account/consumer-secret-placeholder": "consumer secret", + "wikibase-account/access-token-label": "Access token:", + "wikibase-account/access-token-placeholder": "access token", + "wikibase-account/access-secret-label": "Access secret:", + "wikibase-account/access-secret-placeholder": "access secret", + "wikibase-account/remember-me": "Remember me", + "wikibase-account/password-remember-me-title": "Your password won't be stored. Don't check this if your computer is public.", + "wikibase-account/owner-only-consumer-remember-me-title": "Consumer credentials are stored unencrypted in cookies. Don't check this if your computer is public.", + "wikibase-account/close": "Close", + "wikibase-account/log-in": "Log in", + "wikibase-account/logged-in-as": "You are logged in as:", + "wikibase-account/log-out": "Log out", + "wikibase-preview/new-id": "new item", + "wikibase-schema/unsaved-warning": "You have made unsaved changes to your Wikidata schema. Close anyway?", + "wikibase-schema/incomplete-schema-could-not-be-saved": "Your schema is incomplete so it cannot be saved yet.", + "wikibase-schema/discard-schema-changes-alt": "Discard the changes made to the schema.", + "wikibase-schema/save-schema-alt": "Save the schema to OpenRefine. The changes will not be uploaded to Wikidata yet.", + "wikibase-schema/unsaved-changes-alt": "You have made unsaved changes to your Wikidata schema.", + "wikibase-schema/close-button": "Close", + "wikibase-schema/save-button": "Save schema", + "wikibase-schema/discard-button": "Discard changes", + "wikibase-schema/invalid-schema-warning-preview": "Your schema is incomplete, fix it to see the preview.", + "wikibase-schema/invalid-schema-warning-issues": "Your schema is incomplete, fix it to see the issues.", + "wikibase-schema/datatype-not-supported-yet": "This datatype is not supported yet, sorry.", + "wikibase-schema/geoshape-with-prefix": "filename starting with \"Data:\"", + "wikibase-schema/math-expression": "mathematical expression", + "wikibase-schema/commons-media": "filename", + "wikibase-schema/tabular-data-with-prefix": "filename starting with \"Data:\"", + "wikibase-schema/full-url": "full URL including the protocol", + "wikibase-schema/unit": "unit", + "wikibase-schema/amount": "amount", + "wikibase-schema/item-or-reconciled-column": "type item or drag reconciled column here", + "wikibase-schema/alias": "Alias", + "wikibase-schema/override-term": "override if present", + "wikibase-schema/description-override": "Description (override if present)", + "wikibase-schema/description-if-new": "Description (do not override)", + "wikibase-schema/description": "Description", + "wikibase-schema/label-override": "Label (override if present)", + "wikibase-schema/label-if-new": "Label (do not override)", + "wikibase-schema/label": "Label", + "wikibase-schema/remove-column": "remove column", + "wikibase-schema/nb-references": " references", + "wikibase-schema/property-placeholder": "property", + "wikibase-schema/add-reference-snak": "add", + "wikibase-schema/add-reference": "add reference", + "wikibase-schema/reference-copied": "copied", + "wikibase-schema/paste-reference": "paste reference", + "wikibase-schema/copy-reference": "copy", + "wikibase-schema/add-qualifier": "add qualifier", + "wikibase-schema/add-value": "add value", + "wikibase-schema/add-statement": "add statement", + "wikibase-schema/remove": "remove", + "wikibase-schema/add-term": "add term", + "wikibase-schema/add-item-button": "add item", + "wikibase-schema/empty-terms": "no labels, descriptions or aliases added", + "wikibase-schema/empty-statements": "no statements added", + "wikibase-schema/terms-header": "Terms", + "wikibase-schema/statements-header": "Statements", + "wikibase-schema/edits-preview-tab-header": "Preview", + "wikibase-schema/warnings-tab-header": "Issues", + "wikibase-schema/schema-tab-header": "Schema", + "wikibase-schema/preview-explanation": "This tab shows the first edits (out of {nb_edits}) that will be made once you upload the changes to Wikidata. You can use facets to inspect the edits on particular items.", + "wikibase-schema/dialog-explanation": "The Wikidata schema below specifies how your tabular data will be transformed into Wikidata edits. You can drag and drop the column names below in most input boxes: for each row, edits will be generated with the values in these columns.", + "wikibase-schema/dialog-header": "Align to Wikidata", + "wikibase-extension/quickstatements-export-name": "QuickStatements", + "wikibase-extension/wikibase-schema": "Wikidata schema", + "wikibase-extension/export-wikibase-schema": "Export Wikidata schema", + "wikibase-extension/export-schema": "Export schema", + "wikibase-extension/export-to-qs": "QuickStatements file...", + "wikibase-extension/perform-edits-on-wikibase": "Wikidata edits...", + "wikibase-extension/manage-wikibase-account": "Manage Wikidata account", + "wikibase-extension/import-wikibase-schema": "Import schema", + "wikibase-extension/edit-wikibase-schema": "Edit Wikidata schema", + "wikibase-extension/menu-label": "Wikidata" } diff --git a/extensions/wikidata/module/langs/translation-es.json b/extensions/wikidata/module/langs/translation-es.json index f4175cb60..c3b563052 100644 --- a/extensions/wikidata/module/langs/translation-es.json +++ b/extensions/wikidata/module/langs/translation-es.json @@ -68,8 +68,8 @@ "warnings-messages/remove-statements-with-invalid-format/title": "Se quitaron declaraciones con formato incorrecto.", "warnings-messages/add-statements-with-invalid-format/body": "Los valores de esta propiedad deben encajar con la expresión regular {regex}, lo cual no sucede en {example_value}, que se añadió en {example_item_entity}.", "warnings-messages/add-statements-with-invalid-format/title": "Hay declaraciones en {property_entity} con formato incorrecto.", - "warnings-messages/new-item-without-P31-or-P279/body": "Debería proporcionar una declaración «ejemplar de» (P31) o «subclase de» (P279) por cada elemento que cree, como {example_entity}.", - "warnings-messages/new-item-without-P31-or-P279/title": "Se crearon elementos nuevos sin ningún tipo.", + "warnings-messages/new-item-without-instance-of-or-subclass-of/body": "Debería proporcionar una declaración «ejemplar de» (P31) o «subclase de» (P279) por cada elemento que cree, como {example_entity}.", + "warnings-messages/new-item-without-instance-of-or-subclass-of/title": "Se crearon elementos nuevos sin ningún tipo.", "warnings-messages/new-item-with-deleted-statements/body": "Posiblemente hay un problema en el esquema o en el proyecto.", "warnings-messages/new-item-with-deleted-statements/title": "Se eliminarán declaraciones en elementos nuevos.", "warnings-messages/new-item-without-descriptions/body": "Añadir descripciones para los elementos nuevos como {example_entity} facilitará la desambiguación entre elementos con el mismo nombre.", @@ -83,102 +83,102 @@ "import-wikibase-schema/schema-label": "O desde texto JSON:", "import-wikibase-schema/file-label": "Desde archivo JSON: ", "import-wikibase-schema/dialog-header": "Importar esquema de Wikidata", - "perform-wikidata-edits/analyzing-edits": "Analizando sus ediciones…", - "perform-wikidata-edits/cancel": "Cancelar", - "perform-wikidata-edits/perform-edits": "Cargar ediciones", - "perform-wikidata-edits/edit-summary-placeholder": "describa sus ediciones en unas pocas palabras", - "perform-wikidata-edits/edit-summary-label": "Resumen de edición:", - "perform-wikidata-edits/logged-in-as": "Ha accedido como", - "perform-wikidata-edits/review-your-edits": "Está a punto de cargar {nb_edits} ediciones en Wikidata. Revíselas con cuidado. Antes de enviar lotes de ediciones grandes, pida la revisión de robots.", - "perform-wikidata-edits/dialog-header": "Cargar ediciones en Wikidata", - "wikidata-account/connecting-to-wikidata": "Conectando con Wikidata…", - "wikidata-account/log-out": "Salir", - "wikidata-account/logged-in-as": "Ha accedido como:", - "wikidata-account/log-in": "Acceder", - "wikidata-account/close": "Cerrar", - "wikidata-account/owner-only-consumer-remember-me-title": "Los datos de acceso del cliente se almacenan sin cifrar en «cookies». No active la opción si el equipo es público.", - "wikidata-account/password-remember-me-title": "No se almacenará su contraseña. No active la opción si el equipo es público.", - "wikidata-account/remember-me": "Recordarme", - "wikidata-account/access-secret-placeholder": "secreto de acceso", - "wikidata-account/access-secret-label": "Secreto de acceso:", - "wikidata-account/access-token-placeholder": "ficha de acceso", - "wikidata-account/access-token-label": "Ficha de acceso:", - "wikidata-account/consumer-secret-placeholder": "secreto de consumidor", - "wikidata-account/consumer-secret-label": "Secreto de consumidor:", - "wikidata-account/consumer-token-placeholder": "ficha de consumidor", - "wikidata-account/consumer-token-label": "Ficha de consumidor:", - "wikidata-account/password-placeholder": "contraseña", - "wikidata-account/password-label": "Contraseña:", - "wikidata-account/username-placeholder": "nombre de usuario", - "wikidata-account/username-label": "Nombre de usuario:", - "wikidata-account/invalid-credentials": "Datos de acceso no válidos", - "wikidata-account/explain-owner-only-consumer-login": "También puede acceder con su consumidor de propietario solo.", - "wikidata-account/explain-password-login": "También puede acceder con su usuario y contraseña.", - "wikidata-account/explain-owner-only-consumer-wiki": "Consulte esta página de wiki para conseguir su consumidor de propietario solo, si aún no tiene uno.", - "wikidata-account/explain-log-in": "Acceder a Wikidata le permite cargar sus ediciones directamente desde OpenRefine.", - "wikidata-account/dialog-header": "Cuenta de Wikidata", - "wikidata-preview/new-id": "elemento nuevo", - "wikidata-schema/unsaved-warning": "Ha efectuado cambios en el esquema de Wikidata que no se han guardado. ¿Quiere salir igualmente?", - "wikidata-schema/incomplete-schema-could-not-be-saved": "El esquema está incompleto y no puede guardarse todavía.", - "wikidata-schema/discard-schema-changes-alt": "Descartar cambios efectuados en este esquema.", - "wikidata-schema/save-schema-alt": "Guarde el esquema en OpenRefine. Esto no cargará aún los cambios en Wikidata.", - "wikidata-schema/unsaved-changes-alt": "Ha efectuado cambios en el esquema de Wikidata que no se han guardado.", - "wikidata-schema/close-button": "Cerrar", - "wikidata-schema/save-button": "Guardar esquema", - "wikidata-schema/discard-button": "Descartar cambios", - "wikidata-schema/invalid-schema-warning-preview": "El esquema está incompleto. Corríjalo para ver la previsualización.", - "wikidata-schema/invalid-schema-warning-issues": "El esquema está incompleto. Corríjalo para ver los problemas.", - "wikidata-schema/datatype-not-supported-yet": "Aún no se admite este tipo de dato.", - "wikidata-schema/geoshape-with-prefix": "nombre de archivo que comienza por «Data:»", - "wikidata-schema/math-expression": "expresión matemática", - "wikidata-schema/commons-media": "nombre de archivo", - "wikidata-schema/tabular-data-with-prefix": "nombre de archivo que comienza por «Data:»", - "wikidata-schema/full-url": "URL completo, el protocolo incluido", - "wikidata-schema/unit": "unidad", - "wikidata-schema/amount": "cantidad", - "wikidata-schema/item-or-reconciled-column": "digite un elemento o arrastre una columna cotejada aquí", - "wikidata-schema/alias": "Alias", - "wikidata-schema/override-term": "anular si existe", - "wikidata-schema/description-override": "Descripción (anular si existe)", - "wikidata-schema/description-if-new": "Descripción (no anular)", - "wikidata-schema/description": "Descripción", - "wikidata-schema/label-override": "Etiqueta (anular si existe)", - "wikidata-schema/label-if-new": "Etiqueta (no anular)", - "wikidata-schema/empty-terms": "no se añadió ninguna etiqueta, descripción ni alias", - "wikidata-schema/label": "Etiqueta", - "wikidata-schema/remove-column": "quitar columna", - "wikidata-schema/nb-references": " referencias", - "wikidata-schema/property-placeholder": "propiedad", - "wikidata-schema/add-reference-snak": "añadir", - "wikidata-schema/add-reference": "añadir referencia", - "wikidata-schema/reference-copied": "copiado", - "wikidata-schema/paste-reference": "pegar referencia", - "wikidata-schema/copy-reference": "copiar", - "wikidata-schema/add-qualifier": "añadir calificativo", - "wikidata-schema/add-value": "añadir valor", - "wikidata-schema/add-statement": "añadir declaración", - "wikidata-schema/remove": "quitar", - "wikidata-schema/add-term": "añadir término", - "wikidata-schema/add-item-button": "añadir elemento", - "wikidata-schema/empty-statements": "no se añadió ninguna declaración", - "wikidata-schema/terms-header": "Términos", - "wikidata-schema/statements-header": "Declaraciones", - "wikidata-schema/edits-preview-tab-header": "Previsualización", - "wikidata-schema/warnings-tab-header": "Problemas", - "wikidata-schema/schema-tab-header": "Esquema", - "wikidata-schema/preview-explanation": "Esta pestaña muestra las primeras ediciones (de un total de {nb_edits}) que se realizarán una vez que cargue los cambios en Wikidata. Puede emplear facetas para inspeccionar las ediciones en elementos concretos.", - "wikidata-schema/dialog-explanation": "El esquema de Wikidata siguiente especifica la manera en que sus datos tabulares se transformarán en ediciones a Wikidata. Puede arrastrar y colocar los nombres de las columnas de más abajo sobre la mayoría de los cuadros de entrada: por cada fila, las ediciones se generarán con los valores en esas columnas.", - "wikidata-schema/dialog-header": "Alinear a Wikidata", - "wikidata-extension/quickstatements-export-name": "QuickStatements", - "wikidata-extension/wikidata-schema": "Esquema de Wikidata", - "wikidata-extension/export-wikidata-schema": "Exportar esquema de Wikidata", - "wikidata-extension/export-schema": "Exportar esquema", - "wikidata-extension/wikidata-edits": "Ediciones a Wikidata…", - "wikidata-extension/qs-file": "Archivo de QuickStatements", - "wikidata-extension/export-to-qs": "Exportar a QuickStatements", - "wikidata-extension/perform-edits-on-wikidata": "Cargar ediciones en Wikidata", - "wikidata-extension/manage-wikidata-account": "Gestionar cuenta de Wikidata", - "wikidata-extension/import-wikidata-schema": "Importar esquema", - "wikidata-extension/edit-wikidata-schema": "Editar esquema de Wikidata", - "wikidata-extension/menu-label": "Wikidata" + "perform-wikibase-edits/analyzing-edits": "Analizando sus ediciones…", + "perform-wikibase-edits/cancel": "Cancelar", + "perform-wikibase-edits/perform-edits": "Cargar ediciones", + "perform-wikibase-edits/edit-summary-placeholder": "describa sus ediciones en unas pocas palabras", + "perform-wikibase-edits/edit-summary-label": "Resumen de edición:", + "perform-wikibase-edits/logged-in-as": "Ha accedido como", + "perform-wikibase-edits/review-your-edits": "Está a punto de cargar {nb_edits} ediciones en Wikidata. Revíselas con cuidado. Antes de enviar lotes de ediciones grandes, pida la revisión de robots.", + "perform-wikibase-edits/dialog-header": "Cargar ediciones en Wikidata", + "wikibase-account/connecting-to-wikibase": "Conectando con Wikidata…", + "wikibase-account/log-out": "Salir", + "wikibase-account/logged-in-as": "Ha accedido como:", + "wikibase-account/log-in": "Acceder", + "wikibase-account/close": "Cerrar", + "wikibase-account/owner-only-consumer-remember-me-title": "Los datos de acceso del cliente se almacenan sin cifrar en «cookies». No active la opción si el equipo es público.", + "wikibase-account/password-remember-me-title": "No se almacenará su contraseña. No active la opción si el equipo es público.", + "wikibase-account/remember-me": "Recordarme", + "wikibase-account/access-secret-placeholder": "secreto de acceso", + "wikibase-account/access-secret-label": "Secreto de acceso:", + "wikibase-account/access-token-placeholder": "ficha de acceso", + "wikibase-account/access-token-label": "Ficha de acceso:", + "wikibase-account/consumer-secret-placeholder": "secreto de consumidor", + "wikibase-account/consumer-secret-label": "Secreto de consumidor:", + "wikibase-account/consumer-token-placeholder": "ficha de consumidor", + "wikibase-account/consumer-token-label": "Ficha de consumidor:", + "wikibase-account/password-placeholder": "contraseña", + "wikibase-account/password-label": "Contraseña:", + "wikibase-account/username-placeholder": "nombre de usuario", + "wikibase-account/username-label": "Nombre de usuario:", + "wikibase-account/invalid-credentials": "Datos de acceso no válidos", + "wikibase-account/explain-owner-only-consumer-login": "También puede acceder con su consumidor de propietario solo.", + "wikibase-account/explain-password-login": "También puede acceder con su usuario y contraseña.", + "wikibase-account/explain-owner-only-consumer-wiki": "Consulte esta página de wiki para conseguir su consumidor de propietario solo, si aún no tiene uno.", + "wikibase-account/explain-log-in": "Acceder a Wikidata le permite cargar sus ediciones directamente desde OpenRefine.", + "wikibase-account/dialog-header": "Cuenta de Wikidata", + "wikibase-preview/new-id": "elemento nuevo", + "wikibase-schema/unsaved-warning": "Ha efectuado cambios en el esquema de Wikidata que no se han guardado. ¿Quiere salir igualmente?", + "wikibase-schema/incomplete-schema-could-not-be-saved": "El esquema está incompleto y no puede guardarse todavía.", + "wikibase-schema/discard-schema-changes-alt": "Descartar cambios efectuados en este esquema.", + "wikibase-schema/save-schema-alt": "Guarde el esquema en OpenRefine. Esto no cargará aún los cambios en Wikidata.", + "wikibase-schema/unsaved-changes-alt": "Ha efectuado cambios en el esquema de Wikidata que no se han guardado.", + "wikibase-schema/close-button": "Cerrar", + "wikibase-schema/save-button": "Guardar esquema", + "wikibase-schema/discard-button": "Descartar cambios", + "wikibase-schema/invalid-schema-warning-preview": "El esquema está incompleto. Corríjalo para ver la previsualización.", + "wikibase-schema/invalid-schema-warning-issues": "El esquema está incompleto. Corríjalo para ver los problemas.", + "wikibase-schema/datatype-not-supported-yet": "Aún no se admite este tipo de dato.", + "wikibase-schema/geoshape-with-prefix": "nombre de archivo que comienza por «Data:»", + "wikibase-schema/math-expression": "expresión matemática", + "wikibase-schema/commons-media": "nombre de archivo", + "wikibase-schema/tabular-data-with-prefix": "nombre de archivo que comienza por «Data:»", + "wikibase-schema/full-url": "URL completo, el protocolo incluido", + "wikibase-schema/unit": "unidad", + "wikibase-schema/amount": "cantidad", + "wikibase-schema/item-or-reconciled-column": "digite un elemento o arrastre una columna cotejada aquí", + "wikibase-schema/alias": "Alias", + "wikibase-schema/override-term": "anular si existe", + "wikibase-schema/description-override": "Descripción (anular si existe)", + "wikibase-schema/description-if-new": "Descripción (no anular)", + "wikibase-schema/description": "Descripción", + "wikibase-schema/label-override": "Etiqueta (anular si existe)", + "wikibase-schema/label-if-new": "Etiqueta (no anular)", + "wikibase-schema/empty-terms": "no se añadió ninguna etiqueta, descripción ni alias", + "wikibase-schema/label": "Etiqueta", + "wikibase-schema/remove-column": "quitar columna", + "wikibase-schema/nb-references": " referencias", + "wikibase-schema/property-placeholder": "propiedad", + "wikibase-schema/add-reference-snak": "añadir", + "wikibase-schema/add-reference": "añadir referencia", + "wikibase-schema/reference-copied": "copiado", + "wikibase-schema/paste-reference": "pegar referencia", + "wikibase-schema/copy-reference": "copiar", + "wikibase-schema/add-qualifier": "añadir calificativo", + "wikibase-schema/add-value": "añadir valor", + "wikibase-schema/add-statement": "añadir declaración", + "wikibase-schema/remove": "quitar", + "wikibase-schema/add-term": "añadir término", + "wikibase-schema/add-item-button": "añadir elemento", + "wikibase-schema/empty-statements": "no se añadió ninguna declaración", + "wikibase-schema/terms-header": "Términos", + "wikibase-schema/statements-header": "Declaraciones", + "wikibase-schema/edits-preview-tab-header": "Previsualización", + "wikibase-schema/warnings-tab-header": "Problemas", + "wikibase-schema/schema-tab-header": "Esquema", + "wikibase-schema/preview-explanation": "Esta pestaña muestra las primeras ediciones (de un total de {nb_edits}) que se realizarán una vez que cargue los cambios en Wikidata. Puede emplear facetas para inspeccionar las ediciones en elementos concretos.", + "wikibase-schema/dialog-explanation": "El esquema de Wikidata siguiente especifica la manera en que sus datos tabulares se transformarán en ediciones a Wikidata. Puede arrastrar y colocar los nombres de las columnas de más abajo sobre la mayoría de los cuadros de entrada: por cada fila, las ediciones se generarán con los valores en esas columnas.", + "wikibase-schema/dialog-header": "Alinear a Wikidata", + "wikibase-extension/quickstatements-export-name": "QuickStatements", + "wikibase-extension/wikibase-schema": "Esquema de Wikidata", + "wikibase-extension/export-wikibase-schema": "Exportar esquema de Wikidata", + "wikibase-extension/export-schema": "Exportar esquema", + "wikibase-extension/wikibase-edits": "Ediciones a Wikidata…", + "wikibase-extension/qs-file": "Archivo de QuickStatements", + "wikibase-extension/export-to-qs": "Exportar a QuickStatements", + "wikibase-extension/perform-edits-on-wikibase": "Cargar ediciones en Wikidata", + "wikibase-extension/manage-wikibase-account": "Gestionar cuenta de Wikidata", + "wikibase-extension/import-wikibase-schema": "Importar esquema", + "wikibase-extension/edit-wikibase-schema": "Editar esquema de Wikidata", + "wikibase-extension/menu-label": "Wikidata" } diff --git a/extensions/wikidata/module/langs/translation-fi.json b/extensions/wikidata/module/langs/translation-fi.json index 44db73414..adb963d58 100644 --- a/extensions/wikidata/module/langs/translation-fi.json +++ b/extensions/wikidata/module/langs/translation-fi.json @@ -1,51 +1,51 @@ { - "wikidata-schema/unit": "yksikkö", - "wikidata-schema/amount": "määrä", - "wikidata-schema/item-or-reconciled-column": "kirjoita kohteen nimi tai raahaa yhdistetty sarake", - "wikidata-schema/alias": "Alias", - "wikidata-schema/override-term": "tallenna olemassaolevan päälle", - "wikidata-schema/description-override": "Kuvaus (tallenna olemassaolevan päällä)", - "wikidata-schema/description-if-new": "Kuvaus (älä tallenna olemassaolevan päälle)", - "wikidata-schema/description": "Kuvaus", - "wikidata-schema/label-override": "Nimi (tallenna olemassaolevan päälle)", - "wikidata-schema/label-if-new": "Nimi (älä tallenna olemassaolevan päälle)", - "wikidata-schema/label": "Nimi", - "wikidata-schema/remove-column": "poista sarake", - "wikidata-schema/nb-references": " lähdeviitettä", - "wikidata-schema/property-placeholder": "ominaisuus", - "wikidata-schema/add-reference-snak": "lisää", - "wikidata-schema/add-reference": "lisää lähdeviite", - "wikidata-schema/reference-copied": "kopioitu", - "wikidata-schema/empty-statements": "ei lisättyjä esityksiä", - "wikidata-schema/copy-reference": "kopioi", - "wikidata-schema/paste-reference": "liitä lähdeviite", - "wikidata-schema/add-qualifier": "lisää tarkenne", - "wikidata-schema/add-value": "lisää arvo", - "wikidata-schema/add-statement": "lisää esitys", - "wikidata-schema/remove": "poista", - "wikidata-schema/add-term": "lisää termi", - "wikidata-schema/add-item-button": "lisää kohde", - "wikidata-schema/empty-terms": "ei lisättyjä nimiä, kuvauksia tai aliaksia", - "wikidata-schema/terms-header": "Termit", - "wikidata-schema/statements-header": "Esitykset", - "wikidata-schema/edits-preview-tab-header": "Esikatselu", - "wikidata-schema/warnings-tab-header": "Ongelmat", - "wikidata-schema/schema-tab-header": "Skeema", - "wikidata-schema/preview-explanation": "Tässä välilehdessä näytetään ensimmäiset muokkaukset (yhteensä {nb_edits} muokkauksesta), jotka tehdään, kun lataat muutokset Wikidataan. Fasetteja käyttämällä voit tutkia osajoukkoa muokkauksista.", - "wikidata-schema/dialog-explanation": "Alla näkyvä Wikidata-skeema määrittelee, kuinka taulukkosi tiedot muuntuvat Wikidatan muokkauksiksi. Voit raahata ja pudottaa sarakkeiden nimiä lähes kaikkiin syöttöruutuihin. Wikidataan tallennetaan sarakkeen arvo jokaista riviä kohti.", - "wikidata-extension/edit-wikidata-schema": "Muokkaa Wikidata-skeemaa", - "wikidata-schema/dialog-header": "Sovita Wikidataan", - "wikidata-extension/quickstatements-export-name": "QuickStatements", - "wikidata-extension/wikidata-schema": "Wikidata-skeema", - "wikidata-extension/export-wikidata-schema": "Vie Wikidata-skeema", - "wikidata-extension/export-schema": "Vie skeema", - "wikidata-extension/wikidata-edits": "Wikidata-muokkaukset…", - "wikidata-extension/qs-file": "QuickStatements -tiedosto", - "wikidata-extension/export-to-qs": "Vie QuickStatementsiin", - "wikidata-extension/perform-edits-on-wikidata": "Lataa muokkaukset Wikidataan", - "wikidata-extension/manage-wikidata-account": "Hallinnoi Wikidatan käyttäjätiliä", - "wikidata-extension/import-wikidata-schema": "Tuo skeema", - "wikidata-extension/menu-label": "Wikidata", + "wikibase-schema/unit": "yksikkö", + "wikibase-schema/amount": "määrä", + "wikibase-schema/item-or-reconciled-column": "kirjoita kohteen nimi tai raahaa yhdistetty sarake", + "wikibase-schema/alias": "Alias", + "wikibase-schema/override-term": "tallenna olemassaolevan päälle", + "wikibase-schema/description-override": "Kuvaus (tallenna olemassaolevan päällä)", + "wikibase-schema/description-if-new": "Kuvaus (älä tallenna olemassaolevan päälle)", + "wikibase-schema/description": "Kuvaus", + "wikibase-schema/label-override": "Nimi (tallenna olemassaolevan päälle)", + "wikibase-schema/label-if-new": "Nimi (älä tallenna olemassaolevan päälle)", + "wikibase-schema/label": "Nimi", + "wikibase-schema/remove-column": "poista sarake", + "wikibase-schema/nb-references": " lähdeviitettä", + "wikibase-schema/property-placeholder": "ominaisuus", + "wikibase-schema/add-reference-snak": "lisää", + "wikibase-schema/add-reference": "lisää lähdeviite", + "wikibase-schema/reference-copied": "kopioitu", + "wikibase-schema/empty-statements": "ei lisättyjä esityksiä", + "wikibase-schema/copy-reference": "kopioi", + "wikibase-schema/paste-reference": "liitä lähdeviite", + "wikibase-schema/add-qualifier": "lisää tarkenne", + "wikibase-schema/add-value": "lisää arvo", + "wikibase-schema/add-statement": "lisää esitys", + "wikibase-schema/remove": "poista", + "wikibase-schema/add-term": "lisää termi", + "wikibase-schema/add-item-button": "lisää kohde", + "wikibase-schema/empty-terms": "ei lisättyjä nimiä, kuvauksia tai aliaksia", + "wikibase-schema/terms-header": "Termit", + "wikibase-schema/statements-header": "Esitykset", + "wikibase-schema/edits-preview-tab-header": "Esikatselu", + "wikibase-schema/warnings-tab-header": "Ongelmat", + "wikibase-schema/schema-tab-header": "Skeema", + "wikibase-schema/preview-explanation": "Tässä välilehdessä näytetään ensimmäiset muokkaukset (yhteensä {nb_edits} muokkauksesta), jotka tehdään, kun lataat muutokset Wikidataan. Fasetteja käyttämällä voit tutkia osajoukkoa muokkauksista.", + "wikibase-schema/dialog-explanation": "Alla näkyvä Wikidata-skeema määrittelee, kuinka taulukkosi tiedot muuntuvat Wikidatan muokkauksiksi. Voit raahata ja pudottaa sarakkeiden nimiä lähes kaikkiin syöttöruutuihin. Wikidataan tallennetaan sarakkeen arvo jokaista riviä kohti.", + "wikibase-extension/edit-wikibase-schema": "Muokkaa Wikidata-skeemaa", + "wikibase-schema/dialog-header": "Sovita Wikidataan", + "wikibase-extension/quickstatements-export-name": "QuickStatements", + "wikibase-extension/wikibase-schema": "Wikidata-skeema", + "wikibase-extension/export-wikibase-schema": "Vie Wikidata-skeema", + "wikibase-extension/export-schema": "Vie skeema", + "wikibase-extension/wikibase-edits": "Wikidata-muokkaukset…", + "wikibase-extension/qs-file": "QuickStatements -tiedosto", + "wikibase-extension/export-to-qs": "Vie QuickStatementsiin", + "wikibase-extension/perform-edits-on-wikibase": "Lataa muokkaukset Wikidataan", + "wikibase-extension/manage-wikibase-account": "Hallinnoi Wikidatan käyttäjätiliä", + "wikibase-extension/import-wikibase-schema": "Tuo skeema", + "wikibase-extension/menu-label": "Wikidata", "warnings-messages/new-item-without-descriptions/title": "Uusia kohteita luotiin ilman kuvauksia.", "warnings-messages/new-item-without-labels-or-aliases/body": "Anna kohteelle vähintään yksi nimi, kuten {example_entity}, jotta muut ymmärtävät, mitä kohde esittää.", "warnings-messages/new-item-without-labels-or-aliases/title": "Uusia kohteita luotiin ilman nimeä tai aliasta.", @@ -55,45 +55,45 @@ "import-wikibase-schema/schema-label": "Tai JSON-tekstistä:", "import-wikibase-schema/file-label": "JSON-tiedostosta: ", "import-wikibase-schema/dialog-header": "Tuo Wikidata-skeema", - "perform-wikidata-edits/analyzing-edits": "Analysoidaan muokkauksiasi…", - "perform-wikidata-edits/cancel": "Peru", - "perform-wikidata-edits/perform-edits": "Lataa muokkaukset", - "perform-wikidata-edits/edit-summary-placeholder": "kerro muutama sana muokkauksistasi", - "perform-wikidata-edits/edit-summary-label": "Muokkausyhteenveto:", - "perform-wikidata-edits/logged-in-as": "Olet kirjautuneena nimellä", - "perform-wikidata-edits/dialog-header": "Lataa muokkaukset Wikidataan", - "wikidata-account/connecting-to-wikidata": "Yhdistetään Wikidataan…", - "wikidata-account/log-out": "Kirjaudu ulos", - "wikidata-account/logged-in-as": "Olet kirjautuneena nimellä:", - "wikidata-account/log-in": "Kirjaudu", - "wikidata-account/close": "Sulje", - "wikidata-account/password-remember-me-title": "Salasanaasi ei tallenneta. Älä ruksi tätä jos käytät julkista tietokonetta.", - "wikidata-account/remember-me": "Muista minut", - "wikidata-account/consumer-token-placeholder": "käyttäjän suojausavain", - "wikidata-account/consumer-token-label": "Käyttäjän suojausavain:", - "wikidata-account/password-placeholder": "salasana", - "wikidata-account/password-label": "Salasana:", - "wikidata-account/username-placeholder": "käyttäjänimi", - "wikidata-account/username-label": "Käyttäjänimi:", - "wikidata-account/invalid-credentials": "Käyttäjätiedot eivät kelpaa", - "wikidata-account/explain-password-login": "Voit kirjautua käyttäjätunnuksellasi ja salasanallasi.", - "wikidata-account/explain-log-in": "Kun kirjaudut Wikidataan, voit ladata muokkaukset suoraan OpenRefinesta.", - "wikidata-account/dialog-header": "Wikidata-käyttäjätili", - "wikidata-preview/new-id": "uusi kohde", - "wikidata-schema/unsaved-warning": "Olet tehnyt tallentamattomia muutoksia Wikidata-skeemaan. Suljetaanko silti?", - "wikidata-schema/incomplete-schema-could-not-be-saved": "Skeemasi on keskeneräinen, eikä sitä voi vielä tallentaa.", - "wikidata-schema/discard-schema-changes-alt": "Hylkää skeemaan tehdyt muutokset.", - "wikidata-schema/save-schema-alt": "Tallenna skeema OpenRefineen. Muutoksia ei vielä ladata Wikidataan.", - "wikidata-schema/unsaved-changes-alt": "Olet tehnyt tallentamattomia muutoksia Wikidata-skeemaan.", - "wikidata-schema/close-button": "Sulje", - "wikidata-schema/save-button": "Tallenna skeema", - "wikidata-schema/discard-button": "Hylkää muutokset", - "wikidata-schema/invalid-schema-warning-preview": "Skeemasi on keskeneräinen, täydentämällä näet esikatselun.", - "wikidata-schema/invalid-schema-warning-issues": "Skeemasi on keskeneräinen, täydentämällä saat nähtäväksi ongelmakohdat.", - "wikidata-schema/datatype-not-supported-yet": "Tätä tietotyyppiä ei valitettavasti vielä tueta.", - "wikidata-schema/geoshape-with-prefix": "tiedoston nimi (sisältäen \"Data:\")", - "wikidata-schema/math-expression": "matemaattinen lauseke", - "wikidata-schema/commons-media": "tiedoston nimi", - "wikidata-schema/tabular-data-with-prefix": "tiedoston nimi (sisältäen \"Data:\")", - "wikidata-schema/full-url": "täydellinen URL (sisältäen esim. http:// tai https:// tms.)" + "perform-wikibase-edits/analyzing-edits": "Analysoidaan muokkauksiasi…", + "perform-wikibase-edits/cancel": "Peru", + "perform-wikibase-edits/perform-edits": "Lataa muokkaukset", + "perform-wikibase-edits/edit-summary-placeholder": "kerro muutama sana muokkauksistasi", + "perform-wikibase-edits/edit-summary-label": "Muokkausyhteenveto:", + "perform-wikibase-edits/logged-in-as": "Olet kirjautuneena nimellä", + "perform-wikibase-edits/dialog-header": "Lataa muokkaukset Wikidataan", + "wikibase-account/connecting-to-wikibase": "Yhdistetään Wikidataan…", + "wikibase-account/log-out": "Kirjaudu ulos", + "wikibase-account/logged-in-as": "Olet kirjautuneena nimellä:", + "wikibase-account/log-in": "Kirjaudu", + "wikibase-account/close": "Sulje", + "wikibase-account/password-remember-me-title": "Salasanaasi ei tallenneta. Älä ruksi tätä jos käytät julkista tietokonetta.", + "wikibase-account/remember-me": "Muista minut", + "wikibase-account/consumer-token-placeholder": "käyttäjän suojausavain", + "wikibase-account/consumer-token-label": "Käyttäjän suojausavain:", + "wikibase-account/password-placeholder": "salasana", + "wikibase-account/password-label": "Salasana:", + "wikibase-account/username-placeholder": "käyttäjänimi", + "wikibase-account/username-label": "Käyttäjänimi:", + "wikibase-account/invalid-credentials": "Käyttäjätiedot eivät kelpaa", + "wikibase-account/explain-password-login": "Voit kirjautua käyttäjätunnuksellasi ja salasanallasi.", + "wikibase-account/explain-log-in": "Kun kirjaudut Wikidataan, voit ladata muokkaukset suoraan OpenRefinesta.", + "wikibase-account/dialog-header": "Wikidata-käyttäjätili", + "wikibase-preview/new-id": "uusi kohde", + "wikibase-schema/unsaved-warning": "Olet tehnyt tallentamattomia muutoksia Wikidata-skeemaan. Suljetaanko silti?", + "wikibase-schema/incomplete-schema-could-not-be-saved": "Skeemasi on keskeneräinen, eikä sitä voi vielä tallentaa.", + "wikibase-schema/discard-schema-changes-alt": "Hylkää skeemaan tehdyt muutokset.", + "wikibase-schema/save-schema-alt": "Tallenna skeema OpenRefineen. Muutoksia ei vielä ladata Wikidataan.", + "wikibase-schema/unsaved-changes-alt": "Olet tehnyt tallentamattomia muutoksia Wikidata-skeemaan.", + "wikibase-schema/close-button": "Sulje", + "wikibase-schema/save-button": "Tallenna skeema", + "wikibase-schema/discard-button": "Hylkää muutokset", + "wikibase-schema/invalid-schema-warning-preview": "Skeemasi on keskeneräinen, täydentämällä näet esikatselun.", + "wikibase-schema/invalid-schema-warning-issues": "Skeemasi on keskeneräinen, täydentämällä saat nähtäväksi ongelmakohdat.", + "wikibase-schema/datatype-not-supported-yet": "Tätä tietotyyppiä ei valitettavasti vielä tueta.", + "wikibase-schema/geoshape-with-prefix": "tiedoston nimi (sisältäen \"Data:\")", + "wikibase-schema/math-expression": "matemaattinen lauseke", + "wikibase-schema/commons-media": "tiedoston nimi", + "wikibase-schema/tabular-data-with-prefix": "tiedoston nimi (sisältäen \"Data:\")", + "wikibase-schema/full-url": "täydellinen URL (sisältäen esim. http:// tai https:// tms.)" } diff --git a/extensions/wikidata/module/langs/translation-fr.json b/extensions/wikidata/module/langs/translation-fr.json index 0ee14f082..378cd5daf 100644 --- a/extensions/wikidata/module/langs/translation-fr.json +++ b/extensions/wikidata/module/langs/translation-fr.json @@ -1,78 +1,78 @@ { - "wikidata-extension/menu-label": "Wikidata", - "wikidata-extension/edit-wikidata-schema": "Modifier le schéma Wikidata", - "wikidata-extension/manage-wikidata-account": "Gérer le compte Wikidata", - "wikidata-extension/perform-edits-on-wikidata": "Publier les modifications sur Wikidata", - "wikidata-extension/export-to-qs": "Exporter vers QuickStatements", - "wikidata-extension/quickstatements-export-name": "QuickStatements", - "wikidata-schema/dialog-header": "Aligner à Wikidata", - "wikidata-schema/dialog-explanation": "Le schéma Wikidata ci-dessous spécifie comment vos données sont transformées en modifications sur Wikidata. Vous pouvez glisser-déposer les noms des colonnes ci-dessous dans la plupart des champs: pour chaque ligne, des modifications seront générées avec les valeurs dans ces colonnes.", - "wikidata-schema/preview-explanation": "Cet onglet affiche les premières modifications (sur {nb_edits} modifications au total) qui seront effectuées quand vous publierez les données sur Wikidata. Vous pouvez utiliser les facettes pour inspecter les changements sur des éléments particuliers.", - "wikidata-schema/schema-tab-header": "Schéma", - "wikidata-schema/warnings-tab-header": "Problèmes", - "wikidata-schema/edits-preview-tab-header": "Prévisualisation", - "wikidata-schema/statements-header": "Déclarations", - "wikidata-schema/terms-header": "Termes", - "wikidata-schema/empty-terms": "pas de libellés, descriptions ou alias ajoutés", - "wikidata-schema/add-item-button": "ajouter un élément", - "wikidata-schema/add-term": "ajouter un terme", - "wikidata-schema/remove": "supprimer", - "wikidata-schema/add-statement": "ajouter une déclaration", - "wikidata-schema/add-value": "ajouter une valeur", - "wikidata-schema/add-qualifier": "ajouter un qualificatif", - "wikidata-schema/copy-reference": "copier", - "wikidata-schema/paste-reference": "coller une référence", - "wikidata-schema/reference-copied": "copiée", - "wikidata-schema/add-reference": "ajouter une référence", - "wikidata-schema/add-reference-snak": "ajouter", - "wikidata-schema/property-placeholder": "propriété", - "wikidata-schema/nb-references": " références", - "wikidata-schema/remove-column": "supprimer la colonne", - "wikidata-schema/label-if-new": "Libellé (sans écraser)", - "wikidata-schema/label-override": "Libellé (écrase si déjà présent)", - "wikidata-schema/description": "Description", - "wikidata-schema/description-if-new": "Description (sans écraser)", - "wikidata-schema/description-override": "Description (écrase si déjà présent)", - "wikidata-schema/override-term": "Écrase si déjà présent", - "wikidata-schema/alias": "Alias", - "wikidata-schema/item-or-reconciled-column": "entrer un élément ou déposer une colonne réconciliée ici", - "wikidata-schema/amount": "quantité", - "wikidata-schema/unit": "unité", - "wikidata-schema/full-url": "URL complète avec protocole", - "wikidata-schema/tabular-data-with-prefix": "nom de fichier débutant par \"Data:\"", - "wikidata-schema/commons-media": "nom de fichier", - "wikidata-schema/math-expression": "expression mathématique", - "wikidata-schema/geoshape-with-prefix": "nom de fichier commençant par \"Data:\"", - "wikidata-schema/datatype-not-supported-yet": "Ce type de données n'est pas encore supporté, désolé.", - "wikidata-schema/invalid-schema-warning-issues": "Votre schéma est incomplet, complétez-le pour voir les problèmes.", - "wikidata-schema/invalid-schema-warning-preview": "Votre schéma est incomplet, complétez-le pour la prévisualisation.", - "wikidata-schema/discard-button": "Annuler les modifications", - "wikidata-schema/save-button": "Enregistrer le schéma", - "wikidata-schema/close-button": "Fermer", - "wikidata-schema/unsaved-changes-alt": "Des changements sur le schéma Wikidata n'ont pas été enregistrés.", - "wikidata-schema/save-schema-alt": "Sauvegarder le schéma dans OpenRefine. Cela n'enverra pas de données sur Wikidata.", - "wikidata-schema/discard-schema-changes-alt": "Abandonner les modifications sur le schéma.", - "wikidata-schema/incomplete-schema-could-not-be-saved": "Votre schéma est incomplet, il ne peut donc pas encore être enregistré.", - "wikidata-schema/unsaved-warning": "Les changements sur le schéma Wikidata n'ont pas été sauvegardés. Souhaitez-vous tout de même fermer ?", - "wikidata-schema/empty-statements": "aucune déclaration ajoutée", - "wikidata-preview/new-id": "nouvel élément", - "wikidata-account/dialog-header": "compte Wikidata", - "wikidata-account/explain-log-in": "Se connecter à Wikidata vous permet de publier vos données sur Wikidata depuis OpenRefine.", - "wikidata-account/username-label": "Nom d'utilisateur :", - "wikidata-account/password-label": "Mot de passe :", - "wikidata-account/close": "Fermer", - "wikidata-account/log-in": "Se connecter", - "wikidata-account/logged-in-as": "Vous êtes connecté·e en tant que :", - "wikidata-account/log-out": "Se déconnecter", - "wikidata-account/connecting-to-wikidata": "Connexion à Wikidata…", - "perform-wikidata-edits/dialog-header": "Envoyer les modifications sur Wikidata", - "perform-wikidata-edits/review-your-edits": "Vous êtes sur le point d'envoyer {nb_edits} modifications à Wikidata. Veuillez les vérifier avec soin. Les modifications à grande échelle devraient être approuvées par la communauté.", - "perform-wikidata-edits/logged-in-as": "Vous êtes connecté·e en tant que", - "perform-wikidata-edits/edit-summary-label": "Résumé de modification :", - "perform-wikidata-edits/edit-summary-placeholder": "décrivez vos modifications en quelques mots", - "perform-wikidata-edits/perform-edits": "Envoyer les modifications", - "perform-wikidata-edits/cancel": "Annuler", - "perform-wikidata-edits/analyzing-edits": "Analyse de vos modifications…", + "wikibase-extension/menu-label": "Wikidata", + "wikibase-extension/edit-wikibase-schema": "Modifier le schéma Wikidata", + "wikibase-extension/manage-wikibase-account": "Gérer le compte Wikidata", + "wikibase-extension/perform-edits-on-wikibase": "Publier les modifications sur Wikidata", + "wikibase-extension/export-to-qs": "Exporter vers QuickStatements", + "wikibase-extension/quickstatements-export-name": "QuickStatements", + "wikibase-schema/dialog-header": "Aligner à Wikidata", + "wikibase-schema/dialog-explanation": "Le schéma Wikidata ci-dessous spécifie comment vos données sont transformées en modifications sur Wikidata. Vous pouvez glisser-déposer les noms des colonnes ci-dessous dans la plupart des champs: pour chaque ligne, des modifications seront générées avec les valeurs dans ces colonnes.", + "wikibase-schema/preview-explanation": "Cet onglet affiche les premières modifications (sur {nb_edits} modifications au total) qui seront effectuées quand vous publierez les données sur Wikidata. Vous pouvez utiliser les facettes pour inspecter les changements sur des éléments particuliers.", + "wikibase-schema/schema-tab-header": "Schéma", + "wikibase-schema/warnings-tab-header": "Problèmes", + "wikibase-schema/edits-preview-tab-header": "Prévisualisation", + "wikibase-schema/statements-header": "Déclarations", + "wikibase-schema/terms-header": "Termes", + "wikibase-schema/empty-terms": "pas de libellés, descriptions ou alias ajoutés", + "wikibase-schema/add-item-button": "ajouter un élément", + "wikibase-schema/add-term": "ajouter un terme", + "wikibase-schema/remove": "supprimer", + "wikibase-schema/add-statement": "ajouter une déclaration", + "wikibase-schema/add-value": "ajouter une valeur", + "wikibase-schema/add-qualifier": "ajouter un qualificatif", + "wikibase-schema/copy-reference": "copier", + "wikibase-schema/paste-reference": "coller une référence", + "wikibase-schema/reference-copied": "copiée", + "wikibase-schema/add-reference": "ajouter une référence", + "wikibase-schema/add-reference-snak": "ajouter", + "wikibase-schema/property-placeholder": "propriété", + "wikibase-schema/nb-references": " références", + "wikibase-schema/remove-column": "supprimer la colonne", + "wikibase-schema/label-if-new": "Libellé (sans écraser)", + "wikibase-schema/label-override": "Libellé (écrase si déjà présent)", + "wikibase-schema/description": "Description", + "wikibase-schema/description-if-new": "Description (sans écraser)", + "wikibase-schema/description-override": "Description (écrase si déjà présent)", + "wikibase-schema/override-term": "Écrase si déjà présent", + "wikibase-schema/alias": "Alias", + "wikibase-schema/item-or-reconciled-column": "entrer un élément ou déposer une colonne réconciliée ici", + "wikibase-schema/amount": "quantité", + "wikibase-schema/unit": "unité", + "wikibase-schema/full-url": "URL complète avec protocole", + "wikibase-schema/tabular-data-with-prefix": "nom de fichier débutant par \"Data:\"", + "wikibase-schema/commons-media": "nom de fichier", + "wikibase-schema/math-expression": "expression mathématique", + "wikibase-schema/geoshape-with-prefix": "nom de fichier commençant par \"Data:\"", + "wikibase-schema/datatype-not-supported-yet": "Ce type de données n'est pas encore supporté, désolé.", + "wikibase-schema/invalid-schema-warning-issues": "Votre schéma est incomplet, complétez-le pour voir les problèmes.", + "wikibase-schema/invalid-schema-warning-preview": "Votre schéma est incomplet, complétez-le pour la prévisualisation.", + "wikibase-schema/discard-button": "Annuler les modifications", + "wikibase-schema/save-button": "Enregistrer le schéma", + "wikibase-schema/close-button": "Fermer", + "wikibase-schema/unsaved-changes-alt": "Des changements sur le schéma Wikidata n'ont pas été enregistrés.", + "wikibase-schema/save-schema-alt": "Sauvegarder le schéma dans OpenRefine. Cela n'enverra pas de données sur Wikidata.", + "wikibase-schema/discard-schema-changes-alt": "Abandonner les modifications sur le schéma.", + "wikibase-schema/incomplete-schema-could-not-be-saved": "Votre schéma est incomplet, il ne peut donc pas encore être enregistré.", + "wikibase-schema/unsaved-warning": "Les changements sur le schéma Wikidata n'ont pas été sauvegardés. Souhaitez-vous tout de même fermer ?", + "wikibase-schema/empty-statements": "aucune déclaration ajoutée", + "wikibase-preview/new-id": "nouvel élément", + "wikibase-account/dialog-header": "compte Wikidata", + "wikibase-account/explain-log-in": "Se connecter à Wikidata vous permet de publier vos données sur Wikidata depuis OpenRefine.", + "wikibase-account/username-label": "Nom d'utilisateur :", + "wikibase-account/password-label": "Mot de passe :", + "wikibase-account/close": "Fermer", + "wikibase-account/log-in": "Se connecter", + "wikibase-account/logged-in-as": "Vous êtes connecté·e en tant que :", + "wikibase-account/log-out": "Se déconnecter", + "wikibase-account/connecting-to-wikibase": "Connexion à Wikidata…", + "perform-wikibase-edits/dialog-header": "Envoyer les modifications sur Wikidata", + "perform-wikibase-edits/review-your-edits": "Vous êtes sur le point d'envoyer {nb_edits} modifications à Wikidata. Veuillez les vérifier avec soin. Les modifications à grande échelle devraient être approuvées par la communauté.", + "perform-wikibase-edits/logged-in-as": "Vous êtes connecté·e en tant que", + "perform-wikibase-edits/edit-summary-label": "Résumé de modification :", + "perform-wikibase-edits/edit-summary-placeholder": "décrivez vos modifications en quelques mots", + "perform-wikibase-edits/perform-edits": "Envoyer les modifications", + "perform-wikibase-edits/cancel": "Annuler", + "perform-wikibase-edits/analyzing-edits": "Analyse de vos modifications…", "warnings-messages/new-item-created/title": "Ce groupe de modifications va créer de nouveaux éléments Wikidata.", "warnings-messages/new-item-created/body": "Assurez-vous que ces éléments n'existent pas déjà et qu'ils sont acceptables pour être inclus dans Wikidata.", "warnings-messages/new-item-without-labels-or-aliases/title": "Nouveaux éléments créés sans libellé ou alias.", @@ -81,8 +81,8 @@ "warnings-messages/new-item-without-descriptions/body": "Ajouter des descriptinos sur les nouveaux éléments tels que {example_entity} aidera à les distinguer d'homonymes.", "warnings-messages/new-item-with-deleted-statements/title": "Suppression de déclarations sur des nouveaux éléments.", "warnings-messages/new-item-with-deleted-statements/body": "Cela vient probablement d'une erreur dans votre schéma ou projet.", - "warnings-messages/new-item-without-P31-or-P279/title": "Nouveaux éléments créés sans aucune classe.", - "warnings-messages/new-item-without-P31-or-P279/body": "Il est préférable d'ajouter une déclaration \"nature de l'élément\" (P31) ou \"sous-classe de\" (P279) pour chaque nouvel élément que vous créez, comme {example_entity} par exemple.", + "warnings-messages/new-item-without-instance-of-or-subclass-of/title": "Nouveaux éléments créés sans aucune classe.", + "warnings-messages/new-item-without-instance-of-or-subclass-of/body": "Il est préférable d'ajouter une déclaration \"nature de l'élément\" (P31) ou \"sous-classe de\" (P279) pour chaque nouvel élément que vous créez, comme {example_entity} par exemple.", "warnings-messages/add-statements-with-invalid-format/title": "Déclarations {property_entity} avec un format invalide.", "warnings-messages/add-statements-with-invalid-format/body": "Les valeurs pour cette propriété devraient correspondre à l'expression régulière {regex}, ce qui n'est pas le cas de {example_value} ajouté sur {example_item_entity}.", "warnings-messages/remove-statements-with-invalid-format/title": "Déclarations avec un format invalide supprimées.", @@ -143,21 +143,21 @@ "warnings-messages/no-unit-provided/body": "Des valeurs telles que {example_value} sur {example_item_entity} devraient avoir des unités.", "warnings-messages/invalid-entity-type/title": "{property_entity} utilisée sur des éléments", "warnings-messages/invalid-entity-type/body": "Les utilisations de {property_entity} sur des éléments tels que {example_entity} sont invalides.", - "wikidata-extension/import-wikidata-schema": "Importer un schéma", - "wikidata-extension/export-schema": "Exporter le schéma", - "wikidata-extension/export-wikidata-schema": "Exporter le schéma Wikidata", + "wikibase-extension/import-wikibase-schema": "Importer un schéma", + "wikibase-extension/export-schema": "Exporter le schéma", + "wikibase-extension/export-wikibase-schema": "Exporter le schéma Wikidata", "import-wikibase-schema/dialog-header": "Importer un schéma Wikidata", "import-wikibase-schema/file-label": "A partir du fichier JSON : ", "import-wikibase-schema/schema-label": "Ou à partir d'un texte JSON :", "import-wikibase-schema/invalid-schema": "Schéma Wikibase non valide.", "import-wikibase-schema/import": "Importer", - "wikidata-extension/qs-file": "Fichier QuickStatements", + "wikibase-extension/qs-file": "Fichier QuickStatements", "warnings-messages/item-description-identical-with-label/title": "Description et libellé sont identiques", "warnings-messages/item-description-too-long/body": "La description ({lang}) {description> sur {example_entity} est trop longue. Sa longueur est {length} caractères, la limite étant {max_length}. Les descriptions ne sont pas des phrases entières, mais plutôt des paraphrases succintes de deux à douze mots. Consultez le manuel pour plus d'informations.", "warnings-messages/item-description-too-long/title": "Description trop longue", "warnings-messages/early-gregorian-date/body": "Les dates plus anciennes qu'Octobre 1582 (telles qu'en l'an {example_year}) ne sont probablement pas exprimées en calendrier Grégorien. Consultez le manuel pour spécifier le calendrier approprié pour vos dates.", "warnings-messages/early-gregorian-date/title": "Dates anciennes en calendrier Grégorien", - "wikidata-schema/label": "Libellé", - "wikidata-extension/wikidata-schema": "Schéma Wikidata", - "wikidata-extension/wikidata-edits": "Contributions Wikidata…" + "wikibase-schema/label": "Libellé", + "wikibase-extension/wikibase-schema": "Schéma Wikidata", + "wikibase-extension/wikibase-edits": "Contributions Wikidata…" } diff --git a/extensions/wikidata/module/langs/translation-it.json b/extensions/wikidata/module/langs/translation-it.json index b05f1697f..4b908e923 100644 --- a/extensions/wikidata/module/langs/translation-it.json +++ b/extensions/wikidata/module/langs/translation-it.json @@ -1,74 +1,74 @@ { - "wikidata-extension/edit-wikidata-schema": "Modifica lo schema per Wikidata", - "wikidata-extension/manage-wikidata-account": "Gestisci il tuo account su Wikidata", - "wikidata-extension/perform-edits-on-wikidata": "Carica le tue modifiche su Wikidata", - "wikidata-extension/export-to-qs": "Esporta verso QuickStatements", - "wikidata-extension/quickstatements-export-name": "QuickStatements", - "wikidata-extension/menu-label": "Wikidata", - "wikidata-schema/dialog-explanation": "Lo schema per Wikidata ti permette di trasformare i dati nella tua tabella in modifiche pronte per Wikidata. Puoi trascinare i nomi delle colonne nei box di testo appropriati: in questo modo, per ogni riga verranno generate delle modifiche su Wikidata con i valori presenti.", - "wikidata-schema/preview-explanation": "Questa pagina mostra solo i primi risultati (su {nb_edits}) delle modifiche che verranno effettuate su Wikidata. Puoi usare le faccette per controllare le singole modifiche sugli elementi.", - "wikidata-schema/schema-tab-header": "Schema", - "wikidata-schema/warnings-tab-header": "Problemi", - "wikidata-schema/edits-preview-tab-header": "Anteprima", - "wikidata-schema/statements-header": "Dichiarazioni", - "wikidata-schema/terms-header": "Termini", - "wikidata-schema/empty-statements": "Nessuna dichiarazione generata", - "wikidata-schema/empty-terms": "Nessuna etichetta, descrizione o alias aggiunto", - "wikidata-schema/add-item-button": "aggiungi elemento", - "wikidata-schema/add-term": "aggiungi termine", - "wikidata-schema/remove": "rimuovi", - "wikidata-schema/add-statement": "aggiungi dichiarazione", - "wikidata-schema/add-value": "aggiungi valore", - "wikidata-schema/add-qualifier": "aggiungi qualificatore", - "wikidata-schema/add-reference": "aggiungi fonte", - "wikidata-schema/add-reference-snak": "aggiungi", - "wikidata-schema/property-placeholder": "proprietà", - "wikidata-schema/nb-references": " note", - "wikidata-schema/remove-column": "rimuovi colonna", - "wikidata-schema/label": "Etichetta", - "wikidata-schema/description": "Descrizione", - "wikidata-schema/alias": "Alias", - "wikidata-schema/item-or-reconciled-column": "digita l'elemento o trascina una colonna riconciliata", - "wikidata-schema/amount": "quantità", - "wikidata-schema/unit": "unità", - "wikidata-schema/full-url": "URL completo (compreso http:// o https://)", - "wikidata-schema/tabular-data-with-prefix": "nome del file (compreso \"Data:\")", - "wikidata-schema/commons-media": "nome del file", - "wikidata-schema/math-expression": "espressione matematica", - "wikidata-schema/geoshape-with-prefix": "nome del file (compreso \"Data:\")", - "wikidata-schema/datatype-not-supported-yet": "Ci dispiace, questo tipo di dato non è ancora supportato.", - "wikidata-schema/invalid-schema-warning-issues": "Il tuo schema è incompleto, per favore correggilo per vedere gli errori.", - "wikidata-schema/invalid-schema-warning-preview": "Il tuo schema è incompleto, per favore correggilo per vedere l'anteprima.", - "wikidata-schema/discard-button": "Non salvare le modifiche", - "wikidata-schema/save-button": "Salva lo schema", - "wikidata-schema/close-button": "Chiudi", - "wikidata-schema/unsaved-changes-alt": "Non hai salvato alcune modifiche al tuo schema Wikidata.", - "wikidata-schema/save-schema-alt": "Salva lo schema. Non verrà ancora effettuata alcuna modifica su Wikidata.", - "wikidata-schema/dialog-header": "Confronta con Wikidata", - "wikidata-schema/discard-schema-changes-alt": "Annulla le modifiche che hai apportato allo schema.", - "wikidata-schema/incomplete-schema-could-not-be-saved": "Il tuo schema è incompleto e non può essere ancora salvato.", - "wikidata-schema/unsaved-warning": "Non hai ancora salvato le tue modifiche al tuo schema. Vuoi chiudere lo stesso?", - "wikidata-preview/new-id": "nuovo elemento", - "wikidata-account/dialog-header": "account su Wikidata", - "wikidata-account/explain-log-in": "Accedere a Wikidata ti permette di caricare i tuoi edit direttamente tramite OpenRefine.", - "wikidata-account/username-label": "Nome utente:", - "wikidata-account/password-label": "Password:", - "wikidata-account/close": "Chiudi", - "wikidata-account/log-in": "Entra", - "wikidata-account/logged-in-as": "Sei registrato come:", - "wikidata-account/log-out": "Scollegati", - "wikidata-account/connecting-to-wikidata": "Connessione a Wikidata in corso…", - "perform-wikidata-edits/dialog-header": "Carica le tue modifiche su Wikidata", - "perform-wikidata-edits/review-your-edits": "Stai per caricare {nb_edits} modifiche su Wikidata. Per favore, controllale per bene. Ricorda che grossi caricamenti di dati devono essere validati prima del caricamento.", - "perform-wikidata-edits/logged-in-as": "Sei registrato come", - "perform-wikidata-edits/edit-summary-label": "Oggetto delle modifiche:", - "perform-wikidata-edits/edit-summary-placeholder": "qualche parola per descrivere le tue modifiche", - "wikidata-extension/import-wikidata-schema": "Importa schema", - "wikidata-extension/export-schema": "Esporta schema", - "wikidata-extension/export-wikidata-schema": "Esporta schema Wikidata", - "perform-wikidata-edits/perform-edits": "Carica modifiche", - "perform-wikidata-edits/cancel": "Annulla", - "perform-wikidata-edits/analyzing-edits": "Analisi delle modifiche apportate…", + "wikibase-extension/edit-wikibase-schema": "Modifica lo schema per Wikidata", + "wikibase-extension/manage-wikibase-account": "Gestisci il tuo account su Wikidata", + "wikibase-extension/perform-edits-on-wikibase": "Carica le tue modifiche su Wikidata", + "wikibase-extension/export-to-qs": "Esporta verso QuickStatements", + "wikibase-extension/quickstatements-export-name": "QuickStatements", + "wikibase-extension/menu-label": "Wikidata", + "wikibase-schema/dialog-explanation": "Lo schema per Wikidata ti permette di trasformare i dati nella tua tabella in modifiche pronte per Wikidata. Puoi trascinare i nomi delle colonne nei box di testo appropriati: in questo modo, per ogni riga verranno generate delle modifiche su Wikidata con i valori presenti.", + "wikibase-schema/preview-explanation": "Questa pagina mostra solo i primi risultati (su {nb_edits}) delle modifiche che verranno effettuate su Wikidata. Puoi usare le faccette per controllare le singole modifiche sugli elementi.", + "wikibase-schema/schema-tab-header": "Schema", + "wikibase-schema/warnings-tab-header": "Problemi", + "wikibase-schema/edits-preview-tab-header": "Anteprima", + "wikibase-schema/statements-header": "Dichiarazioni", + "wikibase-schema/terms-header": "Termini", + "wikibase-schema/empty-statements": "Nessuna dichiarazione generata", + "wikibase-schema/empty-terms": "Nessuna etichetta, descrizione o alias aggiunto", + "wikibase-schema/add-item-button": "aggiungi elemento", + "wikibase-schema/add-term": "aggiungi termine", + "wikibase-schema/remove": "rimuovi", + "wikibase-schema/add-statement": "aggiungi dichiarazione", + "wikibase-schema/add-value": "aggiungi valore", + "wikibase-schema/add-qualifier": "aggiungi qualificatore", + "wikibase-schema/add-reference": "aggiungi fonte", + "wikibase-schema/add-reference-snak": "aggiungi", + "wikibase-schema/property-placeholder": "proprietà", + "wikibase-schema/nb-references": " note", + "wikibase-schema/remove-column": "rimuovi colonna", + "wikibase-schema/label": "Etichetta", + "wikibase-schema/description": "Descrizione", + "wikibase-schema/alias": "Alias", + "wikibase-schema/item-or-reconciled-column": "digita l'elemento o trascina una colonna riconciliata", + "wikibase-schema/amount": "quantità", + "wikibase-schema/unit": "unità", + "wikibase-schema/full-url": "URL completo (compreso http:// o https://)", + "wikibase-schema/tabular-data-with-prefix": "nome del file (compreso \"Data:\")", + "wikibase-schema/commons-media": "nome del file", + "wikibase-schema/math-expression": "espressione matematica", + "wikibase-schema/geoshape-with-prefix": "nome del file (compreso \"Data:\")", + "wikibase-schema/datatype-not-supported-yet": "Ci dispiace, questo tipo di dato non è ancora supportato.", + "wikibase-schema/invalid-schema-warning-issues": "Il tuo schema è incompleto, per favore correggilo per vedere gli errori.", + "wikibase-schema/invalid-schema-warning-preview": "Il tuo schema è incompleto, per favore correggilo per vedere l'anteprima.", + "wikibase-schema/discard-button": "Non salvare le modifiche", + "wikibase-schema/save-button": "Salva lo schema", + "wikibase-schema/close-button": "Chiudi", + "wikibase-schema/unsaved-changes-alt": "Non hai salvato alcune modifiche al tuo schema Wikidata.", + "wikibase-schema/save-schema-alt": "Salva lo schema. Non verrà ancora effettuata alcuna modifica su Wikidata.", + "wikibase-schema/dialog-header": "Confronta con Wikidata", + "wikibase-schema/discard-schema-changes-alt": "Annulla le modifiche che hai apportato allo schema.", + "wikibase-schema/incomplete-schema-could-not-be-saved": "Il tuo schema è incompleto e non può essere ancora salvato.", + "wikibase-schema/unsaved-warning": "Non hai ancora salvato le tue modifiche al tuo schema. Vuoi chiudere lo stesso?", + "wikibase-preview/new-id": "nuovo elemento", + "wikibase-account/dialog-header": "account su Wikidata", + "wikibase-account/explain-log-in": "Accedere a Wikidata ti permette di caricare i tuoi edit direttamente tramite OpenRefine.", + "wikibase-account/username-label": "Nome utente:", + "wikibase-account/password-label": "Password:", + "wikibase-account/close": "Chiudi", + "wikibase-account/log-in": "Entra", + "wikibase-account/logged-in-as": "Sei registrato come:", + "wikibase-account/log-out": "Scollegati", + "wikibase-account/connecting-to-wikibase": "Connessione a Wikidata in corso…", + "perform-wikibase-edits/dialog-header": "Carica le tue modifiche su Wikidata", + "perform-wikibase-edits/review-your-edits": "Stai per caricare {nb_edits} modifiche su Wikidata. Per favore, controllale per bene. Ricorda che grossi caricamenti di dati devono essere validati prima del caricamento.", + "perform-wikibase-edits/logged-in-as": "Sei registrato come", + "perform-wikibase-edits/edit-summary-label": "Oggetto delle modifiche:", + "perform-wikibase-edits/edit-summary-placeholder": "qualche parola per descrivere le tue modifiche", + "wikibase-extension/import-wikibase-schema": "Importa schema", + "wikibase-extension/export-schema": "Esporta schema", + "wikibase-extension/export-wikibase-schema": "Esporta schema Wikidata", + "perform-wikibase-edits/perform-edits": "Carica modifiche", + "perform-wikibase-edits/cancel": "Annulla", + "perform-wikibase-edits/analyzing-edits": "Analisi delle modifiche apportate…", "import-wikibase-schema/dialog-header": "Importa schema di Wikidata", "import-wikibase-schema/file-label": "Da file JSON: ", "import-wikibase-schema/schema-label": "O da testo JSON:", @@ -82,8 +82,8 @@ "warnings-messages/new-item-without-descriptions/body": "Per favore, aggiungi una descrizione ai nuovi elementi (per esempio {example_entity}), in modo tale che gli altri possano capire a cosa questo faccia riferimento.", "warnings-messages/new-item-with-deleted-statements/title": "Sto cancellando dichiarazioni sui nuovi elementi.", "warnings-messages/new-item-with-deleted-statements/body": "C'è un problema con il tuo schema o con il tuo progetto.", - "warnings-messages/new-item-without-P31-or-P279/title": "Nuovi elementi creati senza alcuna dichiarazione.", - "warnings-messages/new-item-without-P31-or-P279/body": "Per favore, aggiungi una dichiarazione del tipo \"istanza di\" (P31) o \"sottoclasse di\" (P279) per ogni elemento che crei, come per esempio {example_entity}.", + "warnings-messages/new-item-without-instance-of-or-subclass-of/title": "Nuovi elementi creati senza alcuna dichiarazione.", + "warnings-messages/new-item-without-instance-of-or-subclass-of/body": "Per favore, aggiungi una dichiarazione del tipo \"istanza di\" (P31) o \"sottoclasse di\" (P279) per ogni elemento che crei, come per esempio {example_entity}.", "warnings-messages/add-statements-with-invalid-format/title": "Dichiarazioni {property_entity} con formato invalido.", "warnings-messages/add-statements-with-invalid-format/body": "I valori di questa proprietà dovrebbero rispettare l'espressione regolare {regex}, mentre {example_value} aggiunto a {example_item_entity} non lo rispetta.", "warnings-messages/remove-statements-with-invalid-format/title": "Rimosse dichiarazioni con formato invalido.", @@ -144,14 +144,14 @@ "warnings-messages/no-unit-provided/body": "Valori come {example_value} (aggiunto in {example_item_entity}) dovrebbero avere un'unità di misura corrispondente.", "warnings-messages/invalid-entity-type/title": "{property_entity} usata negli elementi", "warnings-messages/invalid-entity-type/body": "L'uso di {property_entity} in elementi come {example_entity} non è valido.", - "wikidata-schema/copy-reference": "copia", - "wikidata-schema/paste-reference": "incolla fonte", - "wikidata-schema/reference-copied": "copiato", - "wikidata-schema/label-if-new": "Etichetta (non sovrascrivere)", - "wikidata-schema/label-override": "Etichetta (sovrascrivi, se esistente)", - "wikidata-schema/description-if-new": "Descrizione (non sovrascrivere)", - "wikidata-schema/description-override": "Descrizione (sovrascrivi, se esistente)", - "wikidata-schema/override-term": "sovrascrivi, se esistente", + "wikibase-schema/copy-reference": "copia", + "wikibase-schema/paste-reference": "incolla fonte", + "wikibase-schema/reference-copied": "copiato", + "wikibase-schema/label-if-new": "Etichetta (non sovrascrivere)", + "wikibase-schema/label-override": "Etichetta (sovrascrivi, se esistente)", + "wikibase-schema/description-if-new": "Descrizione (non sovrascrivere)", + "wikibase-schema/description-override": "Descrizione (sovrascrivi, se esistente)", + "wikibase-schema/override-term": "sovrascrivi, se esistente", "warnings-messages/early-gregorian-date/title": "Date antiche in calendario gregoriano", "warnings-messages/early-gregorian-date/body": "È improbabile che le date precedenti all'ottobre 1582 (ad esempio, {example_year}) siano espresse utilizzando il calendario gregoriano. Per specificare il calendario appropriato per le date, consulta il manual." } diff --git a/extensions/wikidata/module/langs/translation-jp.json b/extensions/wikidata/module/langs/translation-jp.json index 840de3ac4..ed8896c8d 100644 --- a/extensions/wikidata/module/langs/translation-jp.json +++ b/extensions/wikidata/module/langs/translation-jp.json @@ -1,75 +1,75 @@ { - "wikidata-extension/menu-label": "Wikidata", - "wikidata-extension/edit-wikidata-schema": "Wikidataスキーマを編集", - "wikidata-extension/manage-wikidata-account": "Wikidataアカウントの管理", - "wikidata-extension/perform-edits-on-wikidata": "Wikidata編集をアップロード", - "wikidata-extension/wikidata-edits": "Wikidata編集…", - "wikidata-extension/export-to-qs": "クイックステートメントへ出力", - "wikidata-extension/qs-file": "クイックステートメント・ファイル", - "wikidata-extension/quickstatements-export-name": "クイックステートメント", - "wikidata-extension/import-wikidata-schema": "スキーマのインポート", - "wikidata-extension/export-schema": "スキーマのエクスポート", - "wikidata-schema/dialog-header": "Wikidataにアライン", - "wikidata-schema/dialog-explanation": "以下のスキーマは、あなたの表データをどのようにWikidataの編集項目に変換するかを指定します。カラム名を入力boxにドラッグできます。各行が一つづつ項目に変換されます。", - "wikidata-schema/preview-explanation": "このタブは、アップロードされる({nb_edits}のうち)最初の編集項目を表示します。ファセットを使って点検できます。", - "wikidata-schema/schema-tab-header": "スキーマ", - "wikidata-schema/warnings-tab-header": "項目", - "wikidata-schema/edits-preview-tab-header": "プレビュー", - "wikidata-schema/statements-header": "ステートメント", - "wikidata-schema/terms-header": "用語(Term)", - "wikidata-schema/empty-statements": "ステートメントがありません", - "wikidata-schema/empty-terms": "ラベルもdescriptions も別名もありません", - "wikidata-schema/add-item-button": "項目追加", - "wikidata-schema/add-term": "Termの追加", - "wikidata-schema/remove": "削除", - "wikidata-schema/add-statement": "ステートメント追加", - "wikidata-schema/add-value": "値の追加", - "wikidata-schema/add-qualifier": "修飾子追加", - "wikidata-schema/add-reference": "参照の追加", - "wikidata-schema/add-reference-snak": "追加", - "wikidata-schema/property-placeholder": "属性", - "wikidata-schema/nb-references": " 参照", - "wikidata-schema/remove-column": "カラムの削除", - "wikidata-schema/label": "ラベル", - "wikidata-schema/description": "Description(説明)", - "wikidata-schema/alias": "別名", - "wikidata-schema/item-or-reconciled-column": "項目を入力するか、上のカラムをドラッグしてください", - "wikidata-schema/amount": "量", - "wikidata-schema/unit": "単位", - "wikidata-schema/full-url": "完全URL", - "wikidata-schema/tabular-data-with-prefix": "\"Data:\"で始まるファイル名", - "wikidata-schema/commons-media": "ファイル名", - "wikidata-schema/math-expression": "数学的表現", - "wikidata-schema/geoshape-with-prefix": "\"Data:\"で始まるファイル名", - "wikidata-schema/datatype-not-supported-yet": "このデータ型は未対応です.", - "wikidata-schema/invalid-schema-warning-issues": "スキーマが不完全です。修正してください.", - "wikidata-schema/invalid-schema-warning-preview": "スキーマが不完全です。修正してください.", - "wikidata-schema/discard-button": "変更を破棄", - "wikidata-schema/save-button": "スキーマを保存", - "wikidata-schema/close-button": "閉じる", - "wikidata-schema/unsaved-changes-alt": "スキーマの変更は保存されていません.", - "wikidata-schema/save-schema-alt": "スキーマをローカルのOpenRefineに保存。Wikidataには送信されません.", - "wikidata-schema/discard-schema-changes-alt": "スキーマの変更を破棄.", - "wikidata-schema/incomplete-schema-could-not-be-saved": "スキーマが不完全なので保存できません.", - "wikidata-schema/unsaved-warning": "スキーマの変更を保存できていませんが、とにかく終了しますか?", - "wikidata-preview/new-id": "新しい項目", - "wikidata-account/dialog-header": "Wikidataアカウント", - "wikidata-account/explain-log-in": "OpenRefineから直接データをアップロードするため、Wikidataにログインする.", - "wikidata-account/username-label": "ユーザー名:", - "wikidata-account/password-label": "パスワード:", - "wikidata-account/close": "閉じる", - "wikidata-account/log-in": "ログイン", - "wikidata-account/logged-in-as": "ログイン名:", - "wikidata-account/log-out": "ログアウト", - "wikidata-account/connecting-to-wikidata": "Wikidataに接続中…", - "perform-wikidata-edits/dialog-header": "Wikidataに編集をアップロードする", - "perform-wikidata-edits/review-your-edits": "Wikidataに{nb_edits}項目をアップロードします。大量アップロードはbot reviewを試みてください。", - "perform-wikidata-edits/logged-in-as": "ログイン名:", - "perform-wikidata-edits/edit-summary-label": "編集サマリー:", - "perform-wikidata-edits/edit-summary-placeholder": "編集項目について", - "perform-wikidata-edits/perform-edits": "編集項目のアップロード", - "perform-wikidata-edits/cancel": "中止", - "perform-wikidata-edits/analyzing-edits": "編集項目を分析中…", + "wikibase-extension/menu-label": "Wikidata", + "wikibase-extension/edit-wikibase-schema": "Wikidataスキーマを編集", + "wikibase-extension/manage-wikibase-account": "Wikidataアカウントの管理", + "wikibase-extension/perform-edits-on-wikibase": "Wikidata編集をアップロード", + "wikibase-extension/wikibase-edits": "Wikidata編集…", + "wikibase-extension/export-to-qs": "クイックステートメントへ出力", + "wikibase-extension/qs-file": "クイックステートメント・ファイル", + "wikibase-extension/quickstatements-export-name": "クイックステートメント", + "wikibase-extension/import-wikibase-schema": "スキーマのインポート", + "wikibase-extension/export-schema": "スキーマのエクスポート", + "wikibase-schema/dialog-header": "Wikidataにアライン", + "wikibase-schema/dialog-explanation": "以下のスキーマは、あなたの表データをどのようにWikidataの編集項目に変換するかを指定します。カラム名を入力boxにドラッグできます。各行が一つづつ項目に変換されます。", + "wikibase-schema/preview-explanation": "このタブは、アップロードされる({nb_edits}のうち)最初の編集項目を表示します。ファセットを使って点検できます。", + "wikibase-schema/schema-tab-header": "スキーマ", + "wikibase-schema/warnings-tab-header": "項目", + "wikibase-schema/edits-preview-tab-header": "プレビュー", + "wikibase-schema/statements-header": "ステートメント", + "wikibase-schema/terms-header": "用語(Term)", + "wikibase-schema/empty-statements": "ステートメントがありません", + "wikibase-schema/empty-terms": "ラベルもdescriptions も別名もありません", + "wikibase-schema/add-item-button": "項目追加", + "wikibase-schema/add-term": "Termの追加", + "wikibase-schema/remove": "削除", + "wikibase-schema/add-statement": "ステートメント追加", + "wikibase-schema/add-value": "値の追加", + "wikibase-schema/add-qualifier": "修飾子追加", + "wikibase-schema/add-reference": "参照の追加", + "wikibase-schema/add-reference-snak": "追加", + "wikibase-schema/property-placeholder": "属性", + "wikibase-schema/nb-references": " 参照", + "wikibase-schema/remove-column": "カラムの削除", + "wikibase-schema/label": "ラベル", + "wikibase-schema/description": "Description(説明)", + "wikibase-schema/alias": "別名", + "wikibase-schema/item-or-reconciled-column": "項目を入力するか、上のカラムをドラッグしてください", + "wikibase-schema/amount": "量", + "wikibase-schema/unit": "単位", + "wikibase-schema/full-url": "完全URL", + "wikibase-schema/tabular-data-with-prefix": "\"Data:\"で始まるファイル名", + "wikibase-schema/commons-media": "ファイル名", + "wikibase-schema/math-expression": "数学的表現", + "wikibase-schema/geoshape-with-prefix": "\"Data:\"で始まるファイル名", + "wikibase-schema/datatype-not-supported-yet": "このデータ型は未対応です.", + "wikibase-schema/invalid-schema-warning-issues": "スキーマが不完全です。修正してください.", + "wikibase-schema/invalid-schema-warning-preview": "スキーマが不完全です。修正してください.", + "wikibase-schema/discard-button": "変更を破棄", + "wikibase-schema/save-button": "スキーマを保存", + "wikibase-schema/close-button": "閉じる", + "wikibase-schema/unsaved-changes-alt": "スキーマの変更は保存されていません.", + "wikibase-schema/save-schema-alt": "スキーマをローカルのOpenRefineに保存。Wikidataには送信されません.", + "wikibase-schema/discard-schema-changes-alt": "スキーマの変更を破棄.", + "wikibase-schema/incomplete-schema-could-not-be-saved": "スキーマが不完全なので保存できません.", + "wikibase-schema/unsaved-warning": "スキーマの変更を保存できていませんが、とにかく終了しますか?", + "wikibase-preview/new-id": "新しい項目", + "wikibase-account/dialog-header": "Wikidataアカウント", + "wikibase-account/explain-log-in": "OpenRefineから直接データをアップロードするため、Wikidataにログインする.", + "wikibase-account/username-label": "ユーザー名:", + "wikibase-account/password-label": "パスワード:", + "wikibase-account/close": "閉じる", + "wikibase-account/log-in": "ログイン", + "wikibase-account/logged-in-as": "ログイン名:", + "wikibase-account/log-out": "ログアウト", + "wikibase-account/connecting-to-wikibase": "Wikidataに接続中…", + "perform-wikibase-edits/dialog-header": "Wikidataに編集をアップロードする", + "perform-wikibase-edits/review-your-edits": "Wikidataに{nb_edits}項目をアップロードします。大量アップロードはbot reviewを試みてください。", + "perform-wikibase-edits/logged-in-as": "ログイン名:", + "perform-wikibase-edits/edit-summary-label": "編集サマリー:", + "perform-wikibase-edits/edit-summary-placeholder": "編集項目について", + "perform-wikibase-edits/perform-edits": "編集項目のアップロード", + "perform-wikibase-edits/cancel": "中止", + "perform-wikibase-edits/analyzing-edits": "編集項目を分析中…", "warnings-messages/new-item-created/title": "このバッチ編集は新しい項目を作ります.", "warnings-messages/new-item-created/body": "Wikidataに既存項目がないか、また、Wikidataにふさわしいか、確認してください.", "warnings-messages/new-item-without-labels-or-aliases/title": "ラベルも別名もない項目が作られます.", @@ -78,8 +78,8 @@ "warnings-messages/new-item-without-descriptions/body": "記述がないと、{example_entity}項目が明確になります.", "warnings-messages/new-item-with-deleted-statements/title": "新しい項目のステートメントを削除します.", "warnings-messages/new-item-with-deleted-statements/body": "おそらくスキーマに問題があります.", - "warnings-messages/new-item-without-P31-or-P279/title": "型(Type)のない項目が作られます.", - "warnings-messages/new-item-without-P31-or-P279/body": "(P31)のインスタンスか、(P279)のサブクラスが必要です.", + "warnings-messages/new-item-without-instance-of-or-subclass-of/title": "型(Type)のない項目が作られます.", + "warnings-messages/new-item-without-instance-of-or-subclass-of/body": "(P31)のインスタンスか、(P279)のサブクラスが必要です.", "warnings-messages/add-statements-with-invalid-format/title": "{property_entity} ステートメントが無効なフォーマットです.", "warnings-messages/add-statements-with-invalid-format/body": "この項目は、正規表現の{regex}に合致する必要があります。対象:{example_item_entity}の{example_value}.", "warnings-messages/remove-statements-with-invalid-format/title": "無効なフォーマットのステートメントが削除されました.", @@ -157,15 +157,15 @@ "import-wikibase-schema/schema-label": "あるいは、JSONテキスト:", "import-wikibase-schema/invalid-schema": "無効のWikidataスキーマです。", "import-wikibase-schema/import": "インポート", - "wikidata-schema/copy-reference": "コピー", - "wikidata-schema/paste-reference": "参照貼り付け", - "wikidata-schema/reference-copied": "コピー済み", - "wikidata-schema/label-if-new": "ラベル(上書き不可)", - "wikidata-schema/label-override": "ラベル(存在すれば上書き)", - "wikidata-schema/description-if-new": "Description(上書き不可)", - "wikidata-schema/description-override": "Description(存在すれば上書き)", - "wikidata-schema/override-term": "存在すれば上書き", - "wikidata-extension/export-wikidata-schema": "Wikidataスキーマのエクスポート", + "wikibase-schema/copy-reference": "コピー", + "wikibase-schema/paste-reference": "参照貼り付け", + "wikibase-schema/reference-copied": "コピー済み", + "wikibase-schema/label-if-new": "ラベル(上書き不可)", + "wikibase-schema/label-override": "ラベル(存在すれば上書き)", + "wikibase-schema/description-if-new": "Description(上書き不可)", + "wikibase-schema/description-override": "Description(存在すれば上書き)", + "wikibase-schema/override-term": "存在すれば上書き", + "wikibase-extension/export-wikibase-schema": "Wikidataスキーマのエクスポート", "warnings-messages/early-gregorian-date/title": "グレゴリー暦以前の日付", "warnings-messages/early-gregorian-date/body": "1582年10月以前の日付は(例えば、 {example_year}年)、グレゴリー暦で表示されることは稀です。詳しくは、 manual で、適切な暦を指定してください。", "warnings-messages/item-description-too-long/body": "{example_entity}の{description}のようなDescription({lang})は長過ぎです。長さは {length} あり、上限の {max_length} を超えています。Descriptionsはフル表記ではなく、簡潔な説明です。多くの場合、2語から12語までです。詳しくは、マニュアル をご覧ください。", @@ -178,7 +178,7 @@ "warnings-messages/item-description-identical-with-label/body": "{example_entity}のdescription ({lang})とラベル({label_lang})が{description}です。Descriptionは、ラベルよりも詳しい記述です。詳しくはマニュアルをご覧ください。", "warnings-messages/item-description-identical-with-label/title": "Descriptionがラベルと同一です", "warnings-messages/item-description-too-long/title": "Descriptionを短くしてください", - "wikidata-extension/wikidata-schema": "Wikidataスキーマ", + "wikibase-extension/wikibase-schema": "Wikidataスキーマ", "warnings-messages/multi-valued-property-is-required-for-existing-item/body": "このプロパティは2つ以上のステートメントが期待されていますが、例えば {example_entity} には1つしかありません。もしアイテムがWikidataに他のステートメントが存在するなら、この警告は無視して構いません.", "warnings-messages/multi-valued-property-is-required-for-existing-item/title": "{property_entity} は2つ以上のステートメントを持たなければなりません.", "warnings-messages/multi-valued-property-is-required-for-new-item/body": "このプロパティは2つ以上のステートメントが期待されていますが、例えば {example_entity} には1つしかありません.", @@ -204,6 +204,23 @@ "wikidata-account/explain-owner-only-consumer-login": "オーナー限定consumerでもログインできます", "wikidata-account/explain-password-login": "ユーザー名/パスワードでもログインできます", "wikidata-account/explain-owner-only-consumer-wiki": "オーナー限定comsumerを取得するには、 wiki を参照してください.", + "wikibase-account/owner-only-consumer-remember-me-title": "Consumer証明書はクッキーに保存さます。共有PCでは使用しないでください.", + "wikibase-account/password-remember-me-title": "パスワードは保存されません。共有PCでは使用しないでください.", + "wikibase-account/remember-me": "ログイン情報を記憶", + "wikibase-account/access-secret-placeholder": "アクセスの秘密", + "wikibase-account/access-secret-label": "アクセスの秘密:", + "wikibase-account/access-token-placeholder": "アクセストークン", + "wikibase-account/access-token-label": "アクセストークン:", + "wikibase-account/consumer-secret-placeholder": "consumerの秘密", + "wikibase-account/consumer-secret-label": "Consumerの秘密:", + "wikibase-account/consumer-token-placeholder": "consumerトークン", + "wikibase-account/consumer-token-label": "Consumerトークン:", + "wikibase-account/password-placeholder": "パスワード", + "wikibase-account/username-placeholder": "ユーザー名", + "wikibase-account/invalid-credentials": "無効な証明書", + "wikibase-account/explain-owner-only-consumer-login": "オーナー限定consumerでもログインできます", + "wikibase-account/explain-password-login": "ユーザー名/パスワードでもログインできます", + "wikibase-account/explain-owner-only-consumer-wiki": "オーナー限定comsumerを取得するには、 wiki を参照してください.", "warnings-messages/existing-item-requires-certain-other-statement/body": "このプロパティは、例えば {example_entity} で、プロパティ {added_property_entity} を持つ別のステートメントを持つことが想定されています。もし、Wikidataにプロパティ {added_property_entity} があるステートメントを持っている場合、この警告は無視しても構いません.", "warnings-messages/existing-item-requires-certain-other-statement/title": "{property_entity} は、プロパティ {added_property_entity} を持つステートメントが必要です.", "warnings-messages/existing-item-requires-property-to-have-certain-values/body": "プロパティ {added_property_entity} が、例えば {example_entity} で適切な値を取っていません。もし、プロパティ {added_property_entity}とWikidataに適切な値を持つステートメントを持つ場合、この警告は無視しても構いません.", diff --git a/extensions/wikidata/module/langs/translation-ko.json b/extensions/wikidata/module/langs/translation-ko.json index 935080c29..8ffc8feda 100644 --- a/extensions/wikidata/module/langs/translation-ko.json +++ b/extensions/wikidata/module/langs/translation-ko.json @@ -1,73 +1,73 @@ { - "wikidata-extension/menu-label": "Wikidata", - "wikidata-extension/edit-wikidata-schema": "Wikidata 스키마 편집", - "wikidata-extension/import-wikidata-schema": "스키마 임포트", - "wikidata-extension/manage-wikidata-account": "Wikidata 계정 관리", - "wikidata-extension/perform-edits-on-wikidata": "편집한 것을 Wikidata로 업로드", - "wikidata-extension/export-to-qs": "QuickStatements로 익스포트", - "wikidata-extension/export-schema": "스키마 익스포트", - "wikidata-extension/quickstatements-export-name": "QuickStatements", - "wikidata-schema/dialog-header": "Wikidata에 맞추기", - "wikidata-schema/dialog-explanation": "아래의 Wikidata 스키마는 당신의 테이블 데이터가 Wikidata 편집값으로 어떻게 변하는지 지정합니다. 입력상자 아래의 컬럼 이름을 드래그 앤 드랍할 수 있습니다. 매 줄마다, 편집값이 컬럼별로 값이 생성됩니다.", - "wikidata-schema/preview-explanation": "This tab shows the first edits (out of {nb_edits}) that will be made once you upload the changes to Wikidata. You can use facets to inspect the edits on particular items.\n이 탭에서는 Wikidata 로 변경사항을 업로드하면 처음 편집 (out of {nb_edits})이 만들어 집니다. 특정 아이템에서 편집을 검사하기 위해 facets 을 사용할 수 있습니다.", - "wikidata-schema/schema-tab-header": "스키마", - "wikidata-schema/warnings-tab-header": "이슈", - "wikidata-schema/edits-preview-tab-header": "미리보기", - "wikidata-schema/statements-header": "Statements", - "wikidata-schema/terms-header": "용어", - "wikidata-schema/empty-statements": "어떤 statements도 추가 되지 않음", - "wikidata-schema/empty-terms": "어떤 라벨, 설명 및 다른 이름이 추가되지 않음", - "wikidata-schema/add-item-button": "아이템 추가", - "wikidata-schema/add-term": "용어 추가", - "wikidata-schema/remove": "삭제", - "wikidata-schema/add-statement": "statement 추가", - "wikidata-schema/add-value": "값 추가", - "wikidata-schema/add-qualifier": "qualifier 추가", - "wikidata-schema/add-reference": "참조 추가", - "wikidata-schema/add-reference-snak": "추가", - "wikidata-schema/property-placeholder": "속성", - "wikidata-schema/nb-references": " 참조", - "wikidata-schema/remove-column": "컬럼 삭제", - "wikidata-schema/label": "라벨", - "wikidata-schema/description": "설명", - "wikidata-schema/alias": "다른 이름", - "wikidata-schema/item-or-reconciled-column": "아이템을 입력하거나 여기로 조정된 컬럼을 드래그하세요", - "wikidata-schema/amount": "총량", - "wikidata-schema/unit": "단위", - "wikidata-schema/full-url": "프로토콜을 포함하는 전체 URL", - "wikidata-schema/tabular-data-with-prefix": "\"Data:\"로 시작하는 파일이름", - "wikidata-schema/commons-media": "파일이름", - "wikidata-schema/math-expression": "수학적인 표현", - "wikidata-schema/geoshape-with-prefix": "\"Data:\"로 시작하는 파일이름", - "wikidata-schema/datatype-not-supported-yet": "이 데이터타입은 아직 지원하지 않습니다. 미안합니다.", - "wikidata-schema/invalid-schema-warning-issues": "스키마가 불완전합니다. 이슈롤 보기위해서는 스키마를 수정해주세요.", - "wikidata-schema/invalid-schema-warning-preview": "스키마가 불완전합니다. 미리보기를 보려면 스키마를 수정해주세요.", - "wikidata-schema/discard-button": "변경사항 버리기", - "wikidata-schema/save-button": "스키마 저장", - "wikidata-schema/close-button": "닫기", - "wikidata-schema/unsaved-changes-alt": "Wikidata 스키마로 저장되지 않은 변경사항을 만들었습니다.", - "wikidata-schema/save-schema-alt": "OpenRefine으로 스키마를 저장하세요. 변경사항은 Wikidata로 아직 업로드되지 않습니다.", - "wikidata-schema/discard-schema-changes-alt": "스키마로 만들어진 변경사항을 무시하세요.", - "wikidata-schema/incomplete-schema-could-not-be-saved": "스키마가 불완전합니다. 그래서 스키마를 저장할 수 없습니다.", - "wikidata-schema/unsaved-warning": "Wikidata 스키마로 저장되지 않은 변경사항이 만들어졌습니다. 어쨌든 닫을까요?", - "wikidata-preview/new-id": "신규 아이템", - "wikidata-account/dialog-header": "Wikidata 계정", - "wikidata-account/explain-log-in": "Wikidata 로그인하세요. OpenRefine 에서 직접 편집을 업로드할 수 있습니다.", - "wikidata-account/username-label": "사용자이름:", - "wikidata-account/password-label": "비밀번호:", - "wikidata-account/close": "닫기", - "wikidata-account/log-in": "로그인", - "wikidata-account/logged-in-as": "다음으로 로그인 됨:", - "wikidata-account/log-out": "로그아웃", - "wikidata-account/connecting-to-wikidata": "Wikidata 에 접속중 ...", - "perform-wikidata-edits/dialog-header": "Wikidata에 편집을 업로드하기", - "perform-wikidata-edits/review-your-edits": "Wikidata로 {nb_edits} 편집을 업로드하기 시작합니다. 주의해서 체크해주세요. 큰 편집 꾸러미는 봇 리뷰를 위해서 제일 먼저 제출되어야 합니다.", - "perform-wikidata-edits/logged-in-as": "다음으로 로그인 됨", - "perform-wikidata-edits/edit-summary-label": "요약 편집:", - "perform-wikidata-edits/edit-summary-placeholder": "편집을 설명하는 몇가지 단어들", - "perform-wikidata-edits/perform-edits": "편집을 업로드", - "perform-wikidata-edits/cancel": "취소", - "perform-wikidata-edits/analyzing-edits": "편집을 분석중 ...", + "wikibase-extension/menu-label": "Wikidata", + "wikibase-extension/edit-wikibase-schema": "Wikidata 스키마 편집", + "wikibase-extension/import-wikibase-schema": "스키마 임포트", + "wikibase-extension/manage-wikibase-account": "Wikidata 계정 관리", + "wikibase-extension/perform-edits-on-wikibase": "편집한 것을 Wikidata로 업로드", + "wikibase-extension/export-to-qs": "QuickStatements로 익스포트", + "wikibase-extension/export-schema": "스키마 익스포트", + "wikibase-extension/quickstatements-export-name": "QuickStatements", + "wikibase-schema/dialog-header": "Wikidata에 맞추기", + "wikibase-schema/dialog-explanation": "아래의 Wikidata 스키마는 당신의 테이블 데이터가 Wikidata 편집값으로 어떻게 변하는지 지정합니다. 입력상자 아래의 컬럼 이름을 드래그 앤 드랍할 수 있습니다. 매 줄마다, 편집값이 컬럼별로 값이 생성됩니다.", + "wikibase-schema/preview-explanation": "This tab shows the first edits (out of {nb_edits}) that will be made once you upload the changes to Wikidata. You can use facets to inspect the edits on particular items.\n이 탭에서는 Wikidata 로 변경사항을 업로드하면 처음 편집 (out of {nb_edits})이 만들어 집니다. 특정 아이템에서 편집을 검사하기 위해 facets 을 사용할 수 있습니다.", + "wikibase-schema/schema-tab-header": "스키마", + "wikibase-schema/warnings-tab-header": "이슈", + "wikibase-schema/edits-preview-tab-header": "미리보기", + "wikibase-schema/statements-header": "Statements", + "wikibase-schema/terms-header": "용어", + "wikibase-schema/empty-statements": "어떤 statements도 추가 되지 않음", + "wikibase-schema/empty-terms": "어떤 라벨, 설명 및 다른 이름이 추가되지 않음", + "wikibase-schema/add-item-button": "아이템 추가", + "wikibase-schema/add-term": "용어 추가", + "wikibase-schema/remove": "삭제", + "wikibase-schema/add-statement": "statement 추가", + "wikibase-schema/add-value": "값 추가", + "wikibase-schema/add-qualifier": "qualifier 추가", + "wikibase-schema/add-reference": "참조 추가", + "wikibase-schema/add-reference-snak": "추가", + "wikibase-schema/property-placeholder": "속성", + "wikibase-schema/nb-references": " 참조", + "wikibase-schema/remove-column": "컬럼 삭제", + "wikibase-schema/label": "라벨", + "wikibase-schema/description": "설명", + "wikibase-schema/alias": "다른 이름", + "wikibase-schema/item-or-reconciled-column": "아이템을 입력하거나 여기로 조정된 컬럼을 드래그하세요", + "wikibase-schema/amount": "총량", + "wikibase-schema/unit": "단위", + "wikibase-schema/full-url": "프로토콜을 포함하는 전체 URL", + "wikibase-schema/tabular-data-with-prefix": "\"Data:\"로 시작하는 파일이름", + "wikibase-schema/commons-media": "파일이름", + "wikibase-schema/math-expression": "수학적인 표현", + "wikibase-schema/geoshape-with-prefix": "\"Data:\"로 시작하는 파일이름", + "wikibase-schema/datatype-not-supported-yet": "이 데이터타입은 아직 지원하지 않습니다. 미안합니다.", + "wikibase-schema/invalid-schema-warning-issues": "스키마가 불완전합니다. 이슈롤 보기위해서는 스키마를 수정해주세요.", + "wikibase-schema/invalid-schema-warning-preview": "스키마가 불완전합니다. 미리보기를 보려면 스키마를 수정해주세요.", + "wikibase-schema/discard-button": "변경사항 버리기", + "wikibase-schema/save-button": "스키마 저장", + "wikibase-schema/close-button": "닫기", + "wikibase-schema/unsaved-changes-alt": "Wikidata 스키마로 저장되지 않은 변경사항을 만들었습니다.", + "wikibase-schema/save-schema-alt": "OpenRefine으로 스키마를 저장하세요. 변경사항은 Wikidata로 아직 업로드되지 않습니다.", + "wikibase-schema/discard-schema-changes-alt": "스키마로 만들어진 변경사항을 무시하세요.", + "wikibase-schema/incomplete-schema-could-not-be-saved": "스키마가 불완전합니다. 그래서 스키마를 저장할 수 없습니다.", + "wikibase-schema/unsaved-warning": "Wikidata 스키마로 저장되지 않은 변경사항이 만들어졌습니다. 어쨌든 닫을까요?", + "wikibase-preview/new-id": "신규 아이템", + "wikibase-account/dialog-header": "Wikidata 계정", + "wikibase-account/explain-log-in": "Wikidata 로그인하세요. OpenRefine 에서 직접 편집을 업로드할 수 있습니다.", + "wikibase-account/username-label": "사용자이름:", + "wikibase-account/password-label": "비밀번호:", + "wikibase-account/close": "닫기", + "wikibase-account/log-in": "로그인", + "wikibase-account/logged-in-as": "다음으로 로그인 됨:", + "wikibase-account/log-out": "로그아웃", + "wikibase-account/connecting-to-wikibase": "Wikidata 에 접속중 ...", + "perform-wikibase-edits/dialog-header": "Wikidata에 편집을 업로드하기", + "perform-wikibase-edits/review-your-edits": "Wikidata로 {nb_edits} 편집을 업로드하기 시작합니다. 주의해서 체크해주세요. 큰 편집 꾸러미는 봇 리뷰를 위해서 제일 먼저 제출되어야 합니다.", + "perform-wikibase-edits/logged-in-as": "다음으로 로그인 됨", + "perform-wikibase-edits/edit-summary-label": "요약 편집:", + "perform-wikibase-edits/edit-summary-placeholder": "편집을 설명하는 몇가지 단어들", + "perform-wikibase-edits/perform-edits": "편집을 업로드", + "perform-wikibase-edits/cancel": "취소", + "perform-wikibase-edits/analyzing-edits": "편집을 분석중 ...", "import-wikibase-schema/dialog-header": "Wikidata 스키마 임포트", "import-wikibase-schema/file-label": "JSON 파일: ", "import-wikibase-schema/schema-label": "또는 JSON 텍스트:", @@ -81,7 +81,7 @@ "warnings-messages/new-item-without-descriptions/body": "{example_entity}와 같은 신규 아이템에 설명을 추가하면 그 아이템에 대한 모호성을 줄여줄 겁니다.", "warnings-messages/new-item-with-deleted-statements/title": "신규 아이템에서 statements를 삭제.", "warnings-messages/new-item-with-deleted-statements/body": "스키마 또는 프로젝트에 잘못된 것이 아마 있는것 같습니다.", - "warnings-messages/new-item-without-P31-or-P279/title": "타입없이 신규 아이템이 생성되었습니다.", - "warnings-messages/new-item-without-P31-or-P279/body": "{example_entity}와 같이 생성된 각 아이템마다 \"instance of\" (P31) 또는 \"subclass of\" (P279) statement를 작성해야합니다.", + "warnings-messages/new-item-without-instance-of-or-subclass-of/title": "타입없이 신규 아이템이 생성되었습니다.", + "warnings-messages/new-item-without-instance-of-or-subclass-of/body": "{example_entity}와 같이 생성된 각 아이템마다 \"instance of\" (P31) 또는 \"subclass of\" (P279) statement를 작성해야합니다.", "warnings-messages/add-statements-with-invalid-format/title": "{property_entity} statments는 유효하지 않는 포맷을 가지고 있습니다." } diff --git a/extensions/wikidata/module/langs/translation-nb_NO.json b/extensions/wikidata/module/langs/translation-nb_NO.json index e2b00b5fd..871f9ac0d 100644 --- a/extensions/wikidata/module/langs/translation-nb_NO.json +++ b/extensions/wikidata/module/langs/translation-nb_NO.json @@ -1,73 +1,73 @@ { - "wikidata-extension/menu-label": "Wikidata", - "wikidata-extension/edit-wikidata-schema": "Rediger Wikidata-skjema", - "wikidata-extension/import-wikidata-schema": "Importer skjema", - "wikidata-extension/manage-wikidata-account": "Behandle Wikidata-konto", - "wikidata-extension/perform-edits-on-wikidata": "Last opp redigeringer til Wikidata", - "wikidata-extension/export-to-qs": "Eksporter til QuickStatements", - "wikidata-extension/export-schema": "Eksporter skjema", - "wikidata-extension/quickstatements-export-name": "QuickStatements", - "wikidata-schema/dialog-header": "Innrett til Wikidata", - "wikidata-schema/dialog-explanation": "Wikidata-skjemaet nedenfor spesifiserer hvordan dine tabulære data vil transformeres til Wikidata-redigeringer. Du kan dra og slippe kolonnenavnene nedenfor til de fleste inndataboksene: For hver rad vil redigeringer genereres med verdiene i disse kolonnene.", - "wikidata-schema/preview-explanation": "Denne fanen viser de første (av {nb_edits}) redigeringene som vil gjøres når du laster opp endringene til Wikidata. Du kan bruke fasetter for å ettergå redigeringene til spesifikke elementer.", - "wikidata-schema/schema-tab-header": "Skjema", - "wikidata-schema/warnings-tab-header": "Saker", - "wikidata-schema/edits-preview-tab-header": "Forhåndsvisning", - "wikidata-schema/statements-header": "Påstander", - "wikidata-schema/terms-header": "Termer", - "wikidata-schema/empty-statements": "ingen påstander lagt til", - "wikidata-schema/empty-terms": "ingen etiketter, beskrivelser eller aliaser lagt til", - "wikidata-schema/add-item-button": "legg til element", - "wikidata-schema/add-term": "legg til term", - "wikidata-schema/remove": "fjern", - "wikidata-schema/add-statement": "legg til påstand", - "wikidata-schema/add-value": "legg til verdi", - "wikidata-schema/add-qualifier": "legg til kvalifikator", - "wikidata-schema/add-reference": "legg til referanse", - "wikidata-schema/add-reference-snak": "legg til", - "wikidata-schema/property-placeholder": "egenskap", - "wikidata-schema/nb-references": " referanser", - "wikidata-schema/remove-column": "fjern kolonne", - "wikidata-schema/label": "Etikett", - "wikidata-schema/description": "Beskrivelse", - "wikidata-schema/alias": "Alias", - "wikidata-schema/item-or-reconciled-column": "skriv inn element eller dra avstemt kolonne hit", - "wikidata-schema/amount": "antall", - "wikidata-schema/unit": "enhet", - "wikidata-schema/full-url": "full nettadresse, inkludert protokoll", - "wikidata-schema/tabular-data-with-prefix": "filnavn som begynner med «Data:»", - "wikidata-schema/commons-media": "filnavn", - "wikidata-schema/math-expression": "matematisk uttrykk", - "wikidata-schema/geoshape-with-prefix": "filnavn som begynner med «Data:»", - "wikidata-schema/datatype-not-supported-yet": "Denne datatypen støttes ikke ennå.", - "wikidata-schema/invalid-schema-warning-issues": "Skjemaet ditt er ufullstendig, fiks det for å se problemene.", - "wikidata-schema/invalid-schema-warning-preview": "Skjemaet ditt er ufullstendig, fiks det for å se forhåndsvisningen.", - "wikidata-schema/discard-button": "Forkast endringer", - "wikidata-schema/save-button": "Lagre skjema", - "wikidata-schema/close-button": "Lukk", - "wikidata-schema/unsaved-changes-alt": "Du har ulagrede endringer i Wikidata-skjemaet ditt.", - "wikidata-schema/save-schema-alt": "Lagre skjemaet i OpenRefine. Endringene blir ikke lastet opp til Wikidata ennå.", - "wikidata-schema/discard-schema-changes-alt": "Forkast endringene gjort i skjemaet.", - "wikidata-schema/incomplete-schema-could-not-be-saved": "Skjemaet ditt er ufullstendig, så det kan ikke lagres ennå.", - "wikidata-schema/unsaved-warning": "Du har gjort ulagrede endringer i Wikidata-skjemaet ditt. Lukk likevel?", - "wikidata-preview/new-id": "nytt element", - "wikidata-account/dialog-header": "Wikidata-konto", - "wikidata-account/explain-log-in": "Hvis du logger inn på Wikidata kan du laste opp redigeringer direkte fra OpenRefine.", - "wikidata-account/username-label": "Brukernavn:", - "wikidata-account/password-label": "Passord:", - "wikidata-account/close": "Lukk", - "wikidata-account/log-in": "Logg inn", - "wikidata-account/logged-in-as": "Du er innlogget som:", - "wikidata-account/log-out": "Logg ut", - "wikidata-account/connecting-to-wikidata": "Kobler til Wikidata …", - "perform-wikidata-edits/dialog-header": "Last opp redigeringer på Wikidata", - "perform-wikidata-edits/review-your-edits": "Du er i ferd med å laste opp {nb_edits} redigeringer på Wikidata. Sjekk dem nøye. Store redigeringspartier bør gå gjennom bot-godkjenning først.", - "perform-wikidata-edits/logged-in-as": "Du er innlogget som", - "perform-wikidata-edits/edit-summary-label": "Redigeringsforklaring:", - "perform-wikidata-edits/edit-summary-placeholder": "noen ord som beskriver redigeringene dine", - "perform-wikidata-edits/perform-edits": "Last opp redigeringer", - "perform-wikidata-edits/cancel": "Avbryt", - "perform-wikidata-edits/analyzing-edits": "Analyserer redigeringene dine …", + "wikibase-extension/menu-label": "Wikidata", + "wikibase-extension/edit-wikibase-schema": "Rediger Wikidata-skjema", + "wikibase-extension/import-wikibase-schema": "Importer skjema", + "wikibase-extension/manage-wikibase-account": "Behandle Wikidata-konto", + "wikibase-extension/perform-edits-on-wikibase": "Last opp redigeringer til Wikidata", + "wikibase-extension/export-to-qs": "Eksporter til QuickStatements", + "wikibase-extension/export-schema": "Eksporter skjema", + "wikibase-extension/quickstatements-export-name": "QuickStatements", + "wikibase-schema/dialog-header": "Innrett til Wikidata", + "wikibase-schema/dialog-explanation": "Wikidata-skjemaet nedenfor spesifiserer hvordan dine tabulære data vil transformeres til Wikidata-redigeringer. Du kan dra og slippe kolonnenavnene nedenfor til de fleste inndataboksene: For hver rad vil redigeringer genereres med verdiene i disse kolonnene.", + "wikibase-schema/preview-explanation": "Denne fanen viser de første (av {nb_edits}) redigeringene som vil gjøres når du laster opp endringene til Wikidata. Du kan bruke fasetter for å ettergå redigeringene til spesifikke elementer.", + "wikibase-schema/schema-tab-header": "Skjema", + "wikibase-schema/warnings-tab-header": "Saker", + "wikibase-schema/edits-preview-tab-header": "Forhåndsvisning", + "wikibase-schema/statements-header": "Påstander", + "wikibase-schema/terms-header": "Termer", + "wikibase-schema/empty-statements": "ingen påstander lagt til", + "wikibase-schema/empty-terms": "ingen etiketter, beskrivelser eller aliaser lagt til", + "wikibase-schema/add-item-button": "legg til element", + "wikibase-schema/add-term": "legg til term", + "wikibase-schema/remove": "fjern", + "wikibase-schema/add-statement": "legg til påstand", + "wikibase-schema/add-value": "legg til verdi", + "wikibase-schema/add-qualifier": "legg til kvalifikator", + "wikibase-schema/add-reference": "legg til referanse", + "wikibase-schema/add-reference-snak": "legg til", + "wikibase-schema/property-placeholder": "egenskap", + "wikibase-schema/nb-references": " referanser", + "wikibase-schema/remove-column": "fjern kolonne", + "wikibase-schema/label": "Etikett", + "wikibase-schema/description": "Beskrivelse", + "wikibase-schema/alias": "Alias", + "wikibase-schema/item-or-reconciled-column": "skriv inn element eller dra avstemt kolonne hit", + "wikibase-schema/amount": "antall", + "wikibase-schema/unit": "enhet", + "wikibase-schema/full-url": "full nettadresse, inkludert protokoll", + "wikibase-schema/tabular-data-with-prefix": "filnavn som begynner med «Data:»", + "wikibase-schema/commons-media": "filnavn", + "wikibase-schema/math-expression": "matematisk uttrykk", + "wikibase-schema/geoshape-with-prefix": "filnavn som begynner med «Data:»", + "wikibase-schema/datatype-not-supported-yet": "Denne datatypen støttes ikke ennå.", + "wikibase-schema/invalid-schema-warning-issues": "Skjemaet ditt er ufullstendig, fiks det for å se problemene.", + "wikibase-schema/invalid-schema-warning-preview": "Skjemaet ditt er ufullstendig, fiks det for å se forhåndsvisningen.", + "wikibase-schema/discard-button": "Forkast endringer", + "wikibase-schema/save-button": "Lagre skjema", + "wikibase-schema/close-button": "Lukk", + "wikibase-schema/unsaved-changes-alt": "Du har ulagrede endringer i Wikidata-skjemaet ditt.", + "wikibase-schema/save-schema-alt": "Lagre skjemaet i OpenRefine. Endringene blir ikke lastet opp til Wikidata ennå.", + "wikibase-schema/discard-schema-changes-alt": "Forkast endringene gjort i skjemaet.", + "wikibase-schema/incomplete-schema-could-not-be-saved": "Skjemaet ditt er ufullstendig, så det kan ikke lagres ennå.", + "wikibase-schema/unsaved-warning": "Du har gjort ulagrede endringer i Wikidata-skjemaet ditt. Lukk likevel?", + "wikibase-preview/new-id": "nytt element", + "wikibase-account/dialog-header": "Wikidata-konto", + "wikibase-account/explain-log-in": "Hvis du logger inn på Wikidata kan du laste opp redigeringer direkte fra OpenRefine.", + "wikibase-account/username-label": "Brukernavn:", + "wikibase-account/password-label": "Passord:", + "wikibase-account/close": "Lukk", + "wikibase-account/log-in": "Logg inn", + "wikibase-account/logged-in-as": "Du er innlogget som:", + "wikibase-account/log-out": "Logg ut", + "wikibase-account/connecting-to-wikibase": "Kobler til Wikidata …", + "perform-wikibase-edits/dialog-header": "Last opp redigeringer på Wikidata", + "perform-wikibase-edits/review-your-edits": "Du er i ferd med å laste opp {nb_edits} redigeringer på Wikidata. Sjekk dem nøye. Store redigeringspartier bør gå gjennom bot-godkjenning først.", + "perform-wikibase-edits/logged-in-as": "Du er innlogget som", + "perform-wikibase-edits/edit-summary-label": "Redigeringsforklaring:", + "perform-wikibase-edits/edit-summary-placeholder": "noen ord som beskriver redigeringene dine", + "perform-wikibase-edits/perform-edits": "Last opp redigeringer", + "perform-wikibase-edits/cancel": "Avbryt", + "perform-wikibase-edits/analyzing-edits": "Analyserer redigeringene dine …", "import-wikibase-schema/dialog-header": "Importer Wikidata-skjema", "import-wikibase-schema/file-label": "Fra JSON-fil: ", "import-wikibase-schema/schema-label": "Eller fra JSON-tekst:", @@ -81,8 +81,8 @@ "warnings-messages/new-item-without-descriptions/body": "Å legge til beskrivelser til nye elementer som {example_entity} gjør det enklere å skille elementer fra hverandre om de har samme etikett.", "warnings-messages/new-item-with-deleted-statements/title": "Sletter påstander i nye elementer.", "warnings-messages/new-item-with-deleted-statements/body": "Det er trolig noe galt med skjemaet eller prosjektet ditt.", - "warnings-messages/new-item-without-P31-or-P279/title": "Nye elementer opprettet uten type.", - "warnings-messages/new-item-without-P31-or-P279/body": "Du bør angi påstanden «forekomst av» (P31) eller «underklasse av» (P279) for hvert element du oppretter, som {example_entity}.", + "warnings-messages/new-item-without-instance-of-or-subclass-of/title": "Nye elementer opprettet uten type.", + "warnings-messages/new-item-without-instance-of-or-subclass-of/body": "Du bør angi påstanden «forekomst av» (P31) eller «underklasse av» (P279) for hvert element du oppretter, som {example_entity}.", "warnings-messages/add-statements-with-invalid-format/title": "{property_entity} påstander med ugyldig format.", "warnings-messages/add-statements-with-invalid-format/body": "Verdiene for denne egenskapen forventes å samsvare med det regulære uttrykket {regex}, noe som ikke er tilfelle for {example_value} lagt til på {example_item_entity}.", "warnings-messages/remove-statements-with-invalid-format/title": "Fjernet påstander med ugyldig format.", @@ -143,30 +143,30 @@ "warnings-messages/no-unit-provided/body": "Verdier som {example_value} i {example_item_entity} forventes å ha enheter.", "warnings-messages/invalid-entity-type/title": "{property_entity} brukt i elementer", "warnings-messages/invalid-entity-type/body": "Bruken av {property_entity} i elementer som {example_entity} er ugyldig.", - "wikidata-schema/copy-reference": "kopier", - "wikidata-schema/paste-reference": "lim inn referanse", - "wikidata-schema/reference-copied": "kopiert", + "wikibase-schema/copy-reference": "kopier", + "wikibase-schema/paste-reference": "lim inn referanse", + "wikibase-schema/reference-copied": "kopiert", "warnings-messages/item-description-identical-with-label/title": "Beskrivelsen er identisk med etiketten", "warnings-messages/item-description-too-long/title": "Gjør beskrivelsen kortere", - "wikidata-account/owner-only-consumer-remember-me-title": "Konsument-identitetsdetaljer lagres ukryptert i kaker. Ikke velg dette hvis datamaskinen deles av andre.", - "wikidata-account/password-remember-me-title": "Passordet ditt vil ikke bli lagret. Ikke velg dette hvis datamaskinen deles med andre.", - "wikidata-account/remember-me": "Husk meg", - "wikidata-account/access-secret-placeholder": "tilgangshemmelighet", - "wikidata-account/access-secret-label": "Tilgangshemmelighet:", - "wikidata-account/access-token-placeholder": "tilgangssymbol", - "wikidata-account/access-token-label": "Tilgangssymbol:", - "wikidata-account/consumer-secret-placeholder": "konsument-hemmelighet", - "wikidata-account/consumer-secret-label": "Konsument-hemmelighet:", - "wikidata-account/consumer-token-placeholder": "kosnument-symbol", - "wikidata-account/consumer-token-label": "Konsument-symbol:", - "wikidata-account/password-placeholder": "passord", - "wikidata-account/username-placeholder": "brukernavn", - "wikidata-account/invalid-credentials": "Ugyldige identitetsdetaljer", - "wikidata-schema/override-term": "overstyr hvis satt", - "wikidata-schema/description-override": "Beskrivelse (overstyr hvis satt)", - "wikidata-schema/description-if-new": "Beskrivelse (ikke overstyr)", - "wikidata-schema/label-if-new": "Etikett (ikke overstyr)", - "wikidata-schema/label-override": "Etikett (overstyr hvis satt)", - "wikidata-extension/wikidata-edits": "Wikidata-redigeringer…", - "wikidata-extension/qs-file": "QickStatements-fil" + "wikibase-account/owner-only-consumer-remember-me-title": "Konsument-identitetsdetaljer lagres ukryptert i kaker. Ikke velg dette hvis datamaskinen deles av andre.", + "wikibase-account/password-remember-me-title": "Passordet ditt vil ikke bli lagret. Ikke velg dette hvis datamaskinen deles med andre.", + "wikibase-account/remember-me": "Husk meg", + "wikibase-account/access-secret-placeholder": "tilgangshemmelighet", + "wikibase-account/access-secret-label": "Tilgangshemmelighet:", + "wikibase-account/access-token-placeholder": "tilgangssymbol", + "wikibase-account/access-token-label": "Tilgangssymbol:", + "wikibase-account/consumer-secret-placeholder": "konsument-hemmelighet", + "wikibase-account/consumer-secret-label": "Konsument-hemmelighet:", + "wikibase-account/consumer-token-placeholder": "kosnument-symbol", + "wikibase-account/consumer-token-label": "Konsument-symbol:", + "wikibase-account/password-placeholder": "passord", + "wikibase-account/username-placeholder": "brukernavn", + "wikibase-account/invalid-credentials": "Ugyldige identitetsdetaljer", + "wikibase-schema/override-term": "overstyr hvis satt", + "wikibase-schema/description-override": "Beskrivelse (overstyr hvis satt)", + "wikibase-schema/description-if-new": "Beskrivelse (ikke overstyr)", + "wikibase-schema/label-if-new": "Etikett (ikke overstyr)", + "wikibase-schema/label-override": "Etikett (overstyr hvis satt)", + "wikibase-extension/wikibase-edits": "Wikibase-redigeringer…", + "wikibase-extension/qs-file": "QickStatements-fil" } diff --git a/extensions/wikidata/module/langs/translation-nl.json b/extensions/wikidata/module/langs/translation-nl.json index 9a7ec5d22..55ab0b283 100644 --- a/extensions/wikidata/module/langs/translation-nl.json +++ b/extensions/wikidata/module/langs/translation-nl.json @@ -1,71 +1,71 @@ { - "wikidata-extension/menu-label": "Wikidata", - "wikidata-extension/edit-wikidata-schema": "Wikidata-schema bewerken", - "wikidata-extension/import-wikidata-schema": "Schema importeren", - "wikidata-extension/manage-wikidata-account": "Wikidata-account beheren", - "wikidata-extension/perform-edits-on-wikidata": "Bewerkingen naar Wikidata uploaden", - "wikidata-extension/export-to-qs": "Naar QuickStatements exporteren", - "wikidata-extension/export-schema": "Schema exporteren", - "wikidata-extension/quickstatements-export-name": "QuickStatements", - "wikidata-schema/schema-tab-header": "Schema", - "wikidata-schema/warnings-tab-header": "Problemen", - "wikidata-schema/edits-preview-tab-header": "Voorbeeld", - "wikidata-schema/statements-header": "Verklaringen", - "wikidata-schema/terms-header": "Labels, beschrijvingen en aliassen", - "wikidata-schema/empty-statements": "geen verklaringen toegevoegd", - "wikidata-schema/empty-terms": "geen labels, beschrijvingen of aliassen toegevoegd", - "wikidata-schema/add-item-button": "item toevoegen", - "wikidata-schema/add-term": "label, beschrijving of alias toevoegen", - "wikidata-schema/remove": "verwijderen", - "wikidata-schema/add-statement": "verklaring toevoegen", - "wikidata-schema/add-value": "waarde toevoegen", - "wikidata-schema/add-qualifier": "kwalificatie toevoegen", - "wikidata-schema/copy-reference": "kopiëren", - "wikidata-schema/paste-reference": "bron plakken", - "wikidata-schema/reference-copied": "gekopieerd", - "wikidata-schema/add-reference": "bron toevoegen", - "wikidata-schema/add-reference-snak": "toevoegen", - "wikidata-schema/property-placeholder": "eigenschap", - "wikidata-schema/nb-references": " bronnen", - "wikidata-schema/remove-column": "kolom verwijderen", - "wikidata-schema/label": "Label", - "wikidata-schema/description": "Beschrijving", - "wikidata-schema/alias": "Alias", - "wikidata-schema/amount": "hoeveelheid", - "wikidata-schema/unit": "eenheid", - "wikidata-schema/full-url": "volledige URL inclusief protocol", - "wikidata-schema/tabular-data-with-prefix": "bestandsnaam beginnend met \"Data:\"", - "wikidata-schema/commons-media": "bestandsnaam", - "wikidata-schema/math-expression": "wiskundige uitdrukking", - "wikidata-schema/geoshape-with-prefix": "bestandsnaam beginnend met \"Data:\"", - "wikidata-schema/datatype-not-supported-yet": "Dit gegevenstype wordt nog niet ondersteund.", - "wikidata-schema/invalid-schema-warning-issues": "Uw schema is incompleet, los de bestaande problemen op.", - "wikidata-schema/invalid-schema-warning-preview": "Uw schema is incompleet, los de bestaande problemen op om het voorbeeld te bekijken.", - "wikidata-schema/discard-button": "Wijzigingen negeren", - "wikidata-schema/save-button": "Schema opslaan", - "wikidata-schema/close-button": "Sluiten", - "wikidata-schema/unsaved-changes-alt": "U heeft on-opgeslagen wijzigingen aan uw Wikidata-schema aangebracht.", - "wikidata-schema/save-schema-alt": "Sla het schema op in OpenRefine. De wijzigingen worden nog niet naar Wikidata geüpload.", - "wikidata-schema/discard-schema-changes-alt": "De wijzigingen aan het schema negeren.", - "wikidata-schema/incomplete-schema-could-not-be-saved": "Uw schema is incompleet en kan nog niet worden opgeslagen.", - "wikidata-schema/unsaved-warning": "U heeft on-opgeslagen wijzigingen aan uw Wikidata-schema aangebracht. Toch sluiten?", - "wikidata-preview/new-id": "nieuw item", - "wikidata-account/dialog-header": "Wikidata-account", - "wikidata-account/explain-log-in": "Aanmelden bij Wikidata laat u direct bewerkingen vanuit OpenRefine uploaden.", - "wikidata-account/username-label": "Gebruikersnaam:", - "wikidata-account/password-label": "Wachtwoord:", - "wikidata-account/close": "Sluiten", - "wikidata-account/log-in": "Aanmelden", - "wikidata-account/logged-in-as": "U bent aangemeld als:", - "wikidata-account/log-out": "Afmelden", - "wikidata-account/connecting-to-wikidata": "Verbinding maken met Wikidata...", - "perform-wikidata-edits/dialog-header": "Bewerkingen naar Wikidata uploaden", - "perform-wikidata-edits/logged-in-as": "U bent aangemeld als", - "perform-wikidata-edits/edit-summary-label": "Bewerkingssamenvatting:", - "perform-wikidata-edits/edit-summary-placeholder": "een paar woorden om uw bewerkingen te beschrijven", - "perform-wikidata-edits/perform-edits": "Bewerkingen uploaden", - "perform-wikidata-edits/cancel": "Annuleren", - "perform-wikidata-edits/analyzing-edits": "Uw bewerkingen analyseren...", + "wikibase-extension/menu-label": "Wikidata", + "wikibase-extension/edit-wikibase-schema": "Wikidata-schema bewerken", + "wikibase-extension/import-wikibase-schema": "Schema importeren", + "wikibase-extension/manage-wikibase-account": "Wikidata-account beheren", + "wikibase-extension/perform-edits-on-wikibase": "Bewerkingen naar Wikidata uploaden", + "wikibase-extension/export-to-qs": "Naar QuickStatements exporteren", + "wikibase-extension/export-schema": "Schema exporteren", + "wikibase-extension/quickstatements-export-name": "QuickStatements", + "wikibase-schema/schema-tab-header": "Schema", + "wikibase-schema/warnings-tab-header": "Problemen", + "wikibase-schema/edits-preview-tab-header": "Voorbeeld", + "wikibase-schema/statements-header": "Verklaringen", + "wikibase-schema/terms-header": "Labels, beschrijvingen en aliassen", + "wikibase-schema/empty-statements": "geen verklaringen toegevoegd", + "wikibase-schema/empty-terms": "geen labels, beschrijvingen of aliassen toegevoegd", + "wikibase-schema/add-item-button": "item toevoegen", + "wikibase-schema/add-term": "label, beschrijving of alias toevoegen", + "wikibase-schema/remove": "verwijderen", + "wikibase-schema/add-statement": "verklaring toevoegen", + "wikibase-schema/add-value": "waarde toevoegen", + "wikibase-schema/add-qualifier": "kwalificatie toevoegen", + "wikibase-schema/copy-reference": "kopiëren", + "wikibase-schema/paste-reference": "bron plakken", + "wikibase-schema/reference-copied": "gekopieerd", + "wikibase-schema/add-reference": "bron toevoegen", + "wikibase-schema/add-reference-snak": "toevoegen", + "wikibase-schema/property-placeholder": "eigenschap", + "wikibase-schema/nb-references": " bronnen", + "wikibase-schema/remove-column": "kolom verwijderen", + "wikibase-schema/label": "Label", + "wikibase-schema/description": "Beschrijving", + "wikibase-schema/alias": "Alias", + "wikibase-schema/amount": "hoeveelheid", + "wikibase-schema/unit": "eenheid", + "wikibase-schema/full-url": "volledige URL inclusief protocol", + "wikibase-schema/tabular-data-with-prefix": "bestandsnaam beginnend met \"Data:\"", + "wikibase-schema/commons-media": "bestandsnaam", + "wikibase-schema/math-expression": "wiskundige uitdrukking", + "wikibase-schema/geoshape-with-prefix": "bestandsnaam beginnend met \"Data:\"", + "wikibase-schema/datatype-not-supported-yet": "Dit gegevenstype wordt nog niet ondersteund.", + "wikibase-schema/invalid-schema-warning-issues": "Uw schema is incompleet, los de bestaande problemen op.", + "wikibase-schema/invalid-schema-warning-preview": "Uw schema is incompleet, los de bestaande problemen op om het voorbeeld te bekijken.", + "wikibase-schema/discard-button": "Wijzigingen negeren", + "wikibase-schema/save-button": "Schema opslaan", + "wikibase-schema/close-button": "Sluiten", + "wikibase-schema/unsaved-changes-alt": "U heeft on-opgeslagen wijzigingen aan uw Wikidata-schema aangebracht.", + "wikibase-schema/save-schema-alt": "Sla het schema op in OpenRefine. De wijzigingen worden nog niet naar Wikidata geüpload.", + "wikibase-schema/discard-schema-changes-alt": "De wijzigingen aan het schema negeren.", + "wikibase-schema/incomplete-schema-could-not-be-saved": "Uw schema is incompleet en kan nog niet worden opgeslagen.", + "wikibase-schema/unsaved-warning": "U heeft on-opgeslagen wijzigingen aan uw Wikidata-schema aangebracht. Toch sluiten?", + "wikibase-preview/new-id": "nieuw item", + "wikibase-account/dialog-header": "Wikidata-account", + "wikibase-account/explain-log-in": "Aanmelden bij Wikidata laat u direct bewerkingen vanuit OpenRefine uploaden.", + "wikibase-account/username-label": "Gebruikersnaam:", + "wikibase-account/password-label": "Wachtwoord:", + "wikibase-account/close": "Sluiten", + "wikibase-account/log-in": "Aanmelden", + "wikibase-account/logged-in-as": "U bent aangemeld als:", + "wikibase-account/log-out": "Afmelden", + "wikibase-account/connecting-to-wikibase": "Verbinding maken met Wikidata...", + "perform-wikibase-edits/dialog-header": "Bewerkingen naar Wikidata uploaden", + "perform-wikibase-edits/logged-in-as": "U bent aangemeld als", + "perform-wikibase-edits/edit-summary-label": "Bewerkingssamenvatting:", + "perform-wikibase-edits/edit-summary-placeholder": "een paar woorden om uw bewerkingen te beschrijven", + "perform-wikibase-edits/perform-edits": "Bewerkingen uploaden", + "perform-wikibase-edits/cancel": "Annuleren", + "perform-wikibase-edits/analyzing-edits": "Uw bewerkingen analyseren...", "import-wikibase-schema/dialog-header": "Wikidata-schema importeren", "import-wikibase-schema/file-label": "Van JSON-bestand: ", "import-wikibase-schema/schema-label": "Of van JSON-tekst:", @@ -75,7 +75,7 @@ "warnings-messages/new-item-without-labels-or-aliases/title": "Nieuwe items zullen worden aangemaakt zonder label of alias.", "warnings-messages/new-item-without-descriptions/title": "Nieuwe items worden aangemaakt zonder beschrijving.", "warnings-messages/new-item-with-deleted-statements/title": "Statements op nieuwe items worden verwijderd.", - "warnings-messages/new-item-without-P31-or-P279/title": "Nieuwe items worden aangemaakt zonder type.", + "warnings-messages/new-item-without-instance-of-or-subclass-of/title": "Nieuwe items worden aangemaakt zonder type.", "warnings-messages/add-statements-with-invalid-format/title": "{property_entity} verklaringen met ongeldig formaat.", "warnings-messages/unsourced-statements/title": "Verklaringen zonder bronnen.", "warnings-messages/unsourced-statements/body": "Meeste verklaringen hebben bronnen nodig. U kunt deze makkelijk in het schema invoegen.", diff --git a/extensions/wikidata/module/langs/translation-pt_BR.json b/extensions/wikidata/module/langs/translation-pt_BR.json index 86dd7b7a4..05b87b9d0 100644 --- a/extensions/wikidata/module/langs/translation-pt_BR.json +++ b/extensions/wikidata/module/langs/translation-pt_BR.json @@ -1,14 +1,14 @@ { - "wikidata-extension/quickstatements-export-name": "QuickStatements", - "wikidata-extension/wikidata-schema": "Esquema de Wikidata", - "wikidata-extension/export-wikidata-schema": "Exportar esquema de Wikidata", - "wikidata-extension/export-schema": "Exportar esquema", - "wikidata-extension/wikidata-edits": "Edições de Wikidata…", - "wikidata-extension/qs-file": "Arquivo de QuickStatements", - "wikidata-extension/export-to-qs": "Exportar para QuickStatements", - "wikidata-extension/perform-edits-on-wikidata": "Carregar edições para Wikidata", - "wikidata-extension/manage-wikidata-account": "Gerenciar conta de Wikidata", - "wikidata-extension/import-wikidata-schema": "Importar esquema", - "wikidata-extension/edit-wikidata-schema": "Editar esquema de Wikidata", - "wikidata-extension/menu-label": "Wikidata" + "wikibase-extension/quickstatements-export-name": "QuickStatements", + "wikibase-extension/wikibase-schema": "Esquema de Wikidata", + "wikibase-extension/export-wikibase-schema": "Exportar esquema de Wikidata", + "wikibase-extension/export-schema": "Exportar esquema", + "wikibase-extension/wikibase-edits": "Edições de Wikidata…", + "wikibase-extension/qs-file": "Arquivo de QuickStatements", + "wikibase-extension/export-to-qs": "Exportar para QuickStatements", + "wikibase-extension/perform-edits-on-wikibase": "Carregar edições para Wikidata", + "wikibase-extension/manage-wikibase-account": "Gerenciar conta de Wikidata", + "wikibase-extension/import-wikibase-schema": "Importar esquema", + "wikibase-extension/edit-wikibase-schema": "Editar esquema de Wikidata", + "wikibase-extension/menu-label": "Wikidata" } diff --git a/extensions/wikidata/module/langs/translation-sv.json b/extensions/wikidata/module/langs/translation-sv.json index 434ce0401..a58f5b90e 100644 --- a/extensions/wikidata/module/langs/translation-sv.json +++ b/extensions/wikidata/module/langs/translation-sv.json @@ -1,73 +1,73 @@ { - "wikidata-extension/menu-label": "Wikidata", - "wikidata-extension/edit-wikidata-schema": "Redigera Wikidata-schema", - "wikidata-extension/import-wikidata-schema": "Importera schema", - "wikidata-extension/perform-edits-on-wikidata": "Ladda upp redigeringar till Wikidata", - "wikidata-extension/export-to-qs": "Exportera till QuickStatements", - "wikidata-extension/export-schema": "Exportera schema", - "wikidata-extension/quickstatements-export-name": "QuickStatements", - "wikidata-schema/dialog-explanation": "Wikidata-schemat nedan specificerar hur dina tabulerade data transformeras till Wikidata-redigeringar. Du kan dra och släppa kolumnnamnen nedan till de flesta indataboxarna: för varje rad genereras redigeringar med värdena i dessa kolumner.", - "wikidata-schema/schema-tab-header": "Schema", - "wikidata-schema/edits-preview-tab-header": "Förhandsvisning", - "wikidata-schema/statements-header": "Uttalanden", - "wikidata-schema/empty-statements": "Inga uttalanden tillagda", - "wikidata-schema/empty-terms": "inga etiketter, beskrivningar eller alias tillagda", - "wikidata-schema/remove": "ta bort", - "wikidata-schema/add-statement": "lägg till uttalande", - "wikidata-schema/add-value": "lägg till värde", - "wikidata-schema/add-reference": "lägg till referens", - "wikidata-schema/add-reference-snak": "lägg till", - "wikidata-schema/property-placeholder": "egenskap", - "wikidata-schema/nb-references": " referenser", - "wikidata-schema/remove-column": "ta bort kolumn", - "wikidata-schema/label": "Etikett", - "wikidata-schema/description": "Beskrivning", - "wikidata-schema/alias": "Alias", - "wikidata-schema/amount": "antal", - "wikidata-schema/unit": "enhet", - "wikidata-schema/tabular-data-with-prefix": "filnamn som börjar med «Data:»", - "wikidata-schema/commons-media": "filnamn", - "wikidata-schema/math-expression": "matematiskt uttryck", - "wikidata-schema/geoshape-with-prefix": "filnamn som börjar med «Data:»", - "wikidata-schema/invalid-schema-warning-issues": "Schemat är ofullständigt, fixa detta för att se problemen.", - "wikidata-schema/invalid-schema-warning-preview": "Schemat är ofullständigt, fixa detta för att se förhandsvisningen.", - "wikidata-schema/save-button": "Spara schema", - "wikidata-schema/close-button": "Stäng", - "wikidata-schema/save-schema-alt": "Spara schemat i OpenRefine. Ändringarna kommer inte laddas upp till Wikidata ännu.", - "wikidata-schema/incomplete-schema-could-not-be-saved": "Schemat är ofullständigt, så kan inte sparas ännu.", - "wikidata-schema/unsaved-warning": "Ditt schema har ändringar som ej sparats. Stäng ändå?", - "wikidata-account/dialog-header": "Wikidata-konto", - "wikidata-account/close": "Stäng", - "wikidata-account/log-in": "Logga in", - "wikidata-account/logged-in-as": "Du är inloggad som:", - "wikidata-account/log-out": "Logga ut", - "perform-wikidata-edits/logged-in-as": "Du är inloggad som", - "wikidata-extension/manage-wikidata-account": "hantera Wikidata-konto", - "wikidata-schema/dialog-header": "Anpassa till Wikidata", - "wikidata-schema/preview-explanation": "Den här fliken visar de första ändringarna (av {nb_edits}) som kommer att göras när du laddar upp ändringarna till Wikidata. Du kan använda fasetter för att inspektera ändringarna på specifika objekt.", - "wikidata-schema/warnings-tab-header": "Problem", - "wikidata-schema/terms-header": "Termer", - "wikidata-schema/add-item-button": "lägg till objekt", - "wikidata-schema/add-term": "lägg till term", - "wikidata-schema/add-qualifier": "lägg till kvalifikator", - "wikidata-schema/item-or-reconciled-column": "skriv in objekt eller dra avstämd kolumn hit", - "wikidata-schema/full-url": "full URL inklusive protokoll", - "wikidata-schema/datatype-not-supported-yet": "Denna datatyp stöds tyvärr inte ännu.", - "wikidata-schema/discard-button": "Ignorera ändringar", - "wikidata-schema/unsaved-changes-alt": "Du har olagrade ändringar i ditt Wikidata-schema.", - "wikidata-schema/discard-schema-changes-alt": "Ignorera ändringarna i schemat.", - "wikidata-preview/new-id": "nytt objekt", - "wikidata-account/explain-log-in": "Logga in på Wikidata så att du kan ladda upp redigeringar direkt från OpenRefine.", - "wikidata-account/username-label": "Användarnamn:", - "wikidata-account/password-label": "Lösenord:", - "wikidata-account/connecting-to-wikidata": "Anslut till Wikidata…", - "perform-wikidata-edits/dialog-header": "Överför redigeringar till Wikidata", - "perform-wikidata-edits/review-your-edits": "Du håller på att ladda upp {nb_edits} redigeringar till Wikidata. Kontrollera dem noggrant. Stora antal av ändringar bör skickas till botgranskning först.", - "perform-wikidata-edits/edit-summary-label": "Redigeringssammanfattning:", - "perform-wikidata-edits/edit-summary-placeholder": "några ord som beskriver dina ändringar", - "perform-wikidata-edits/perform-edits": "Överför redigeringar", - "perform-wikidata-edits/cancel": "Avbryt", - "perform-wikidata-edits/analyzing-edits": "Analyserar dina ändringar…", + "wikibase-extension/menu-label": "Wikidata", + "wikibase-extension/edit-wikibase-schema": "Redigera Wikidata-schema", + "wikibase-extension/import-wikibase-schema": "Importera schema", + "wikibase-extension/perform-edits-on-wikibase": "Ladda upp redigeringar till Wikidata", + "wikibase-extension/export-to-qs": "Exportera till QuickStatements", + "wikibase-extension/export-schema": "Exportera schema", + "wikibase-extension/quickstatements-export-name": "QuickStatements", + "wikibase-schema/dialog-explanation": "Wikidata-schemat nedan specificerar hur dina tabulerade data transformeras till Wikidata-redigeringar. Du kan dra och släppa kolumnnamnen nedan till de flesta indataboxarna: för varje rad genereras redigeringar med värdena i dessa kolumner.", + "wikibase-schema/schema-tab-header": "Schema", + "wikibase-schema/edits-preview-tab-header": "Förhandsvisning", + "wikibase-schema/statements-header": "Uttalanden", + "wikibase-schema/empty-statements": "Inga uttalanden tillagda", + "wikibase-schema/empty-terms": "inga etiketter, beskrivningar eller alias tillagda", + "wikibase-schema/remove": "ta bort", + "wikibase-schema/add-statement": "lägg till uttalande", + "wikibase-schema/add-value": "lägg till värde", + "wikibase-schema/add-reference": "lägg till referens", + "wikibase-schema/add-reference-snak": "lägg till", + "wikibase-schema/property-placeholder": "egenskap", + "wikibase-schema/nb-references": " referenser", + "wikibase-schema/remove-column": "ta bort kolumn", + "wikibase-schema/label": "Etikett", + "wikibase-schema/description": "Beskrivning", + "wikibase-schema/alias": "Alias", + "wikibase-schema/amount": "antal", + "wikibase-schema/unit": "enhet", + "wikibase-schema/tabular-data-with-prefix": "filnamn som börjar med «Data:»", + "wikibase-schema/commons-media": "filnamn", + "wikibase-schema/math-expression": "matematiskt uttryck", + "wikibase-schema/geoshape-with-prefix": "filnamn som börjar med «Data:»", + "wikibase-schema/invalid-schema-warning-issues": "Schemat är ofullständigt, fixa detta för att se problemen.", + "wikibase-schema/invalid-schema-warning-preview": "Schemat är ofullständigt, fixa detta för att se förhandsvisningen.", + "wikibase-schema/save-button": "Spara schema", + "wikibase-schema/close-button": "Stäng", + "wikibase-schema/save-schema-alt": "Spara schemat i OpenRefine. Ändringarna kommer inte laddas upp till Wikidata ännu.", + "wikibase-schema/incomplete-schema-could-not-be-saved": "Schemat är ofullständigt, så kan inte sparas ännu.", + "wikibase-schema/unsaved-warning": "Ditt schema har ändringar som ej sparats. Stäng ändå?", + "wikibase-account/dialog-header": "Wikidata-konto", + "wikibase-account/close": "Stäng", + "wikibase-account/log-in": "Logga in", + "wikibase-account/logged-in-as": "Du är inloggad som:", + "wikibase-account/log-out": "Logga ut", + "perform-wikibase-edits/logged-in-as": "Du är inloggad som", + "wikibase-extension/manage-wikibase-account": "hantera Wikidata-konto", + "wikibase-schema/dialog-header": "Anpassa till Wikidata", + "wikibase-schema/preview-explanation": "Den här fliken visar de första ändringarna (av {nb_edits}) som kommer att göras när du laddar upp ändringarna till Wikidata. Du kan använda fasetter för att inspektera ändringarna på specifika objekt.", + "wikibase-schema/warnings-tab-header": "Problem", + "wikibase-schema/terms-header": "Termer", + "wikibase-schema/add-item-button": "lägg till objekt", + "wikibase-schema/add-term": "lägg till term", + "wikibase-schema/add-qualifier": "lägg till kvalifikator", + "wikibase-schema/item-or-reconciled-column": "skriv in objekt eller dra avstämd kolumn hit", + "wikibase-schema/full-url": "full URL inklusive protokoll", + "wikibase-schema/datatype-not-supported-yet": "Denna datatyp stöds tyvärr inte ännu.", + "wikibase-schema/discard-button": "Ignorera ändringar", + "wikibase-schema/unsaved-changes-alt": "Du har olagrade ändringar i ditt Wikidata-schema.", + "wikibase-schema/discard-schema-changes-alt": "Ignorera ändringarna i schemat.", + "wikibase-preview/new-id": "nytt objekt", + "wikibase-account/explain-log-in": "Logga in på Wikidata så att du kan ladda upp redigeringar direkt från OpenRefine.", + "wikibase-account/username-label": "Användarnamn:", + "wikibase-account/password-label": "Lösenord:", + "wikibase-account/connecting-to-wikibase": "Anslut till Wikidata…", + "perform-wikibase-edits/dialog-header": "Överför redigeringar till Wikidata", + "perform-wikibase-edits/review-your-edits": "Du håller på att ladda upp {nb_edits} redigeringar till Wikidata. Kontrollera dem noggrant. Stora antal av ändringar bör skickas till botgranskning först.", + "perform-wikibase-edits/edit-summary-label": "Redigeringssammanfattning:", + "perform-wikibase-edits/edit-summary-placeholder": "några ord som beskriver dina ändringar", + "perform-wikibase-edits/perform-edits": "Överför redigeringar", + "perform-wikibase-edits/cancel": "Avbryt", + "perform-wikibase-edits/analyzing-edits": "Analyserar dina ändringar…", "import-wikibase-schema/dialog-header": "Importera Wikidata-schema", "import-wikibase-schema/file-label": "Från JSON-fil: ", "import-wikibase-schema/schema-label": "Eller från JSON-text:", @@ -81,8 +81,8 @@ "warnings-messages/new-item-without-descriptions/body": "Lägga till beskrivningar för nya objekt som {example_entity} gör det enklare att skilja objekten från andra som har samma etikett.", "warnings-messages/new-item-with-deleted-statements/title": "Radera påståenden om nya objekt.", "warnings-messages/new-item-with-deleted-statements/body": "Det finns förmodligen något fel i ditt schema eller projekt.", - "warnings-messages/new-item-without-P31-or-P279/title": "Nya objekt skapade utan typ.", - "warnings-messages/new-item-without-P31-or-P279/body": "Du bör ange \"instans av\" (P31) eller \"underklass till\" (P279) för varje objekt som du skapar, till exempel {example_entity}.", + "warnings-messages/new-item-without-instance-of-or-subclass-of/title": "Nya objekt skapade utan typ.", + "warnings-messages/new-item-without-instance-of-or-subclass-of/body": "Du bör ange \"instans av\" (P31) eller \"underklass till\" (P279) för varje objekt som du skapar, till exempel {example_entity}.", "warnings-messages/add-statements-with-invalid-format/title": "{property_entity} påstående med ogiltigt format.", "warnings-messages/add-statements-with-invalid-format/body": "Värden för den här egenskapen förväntas matcha det reguljära uttrycket {regex}, vilket inte är fallet för { example_value} tillagt på {example_item_entity}.", "warnings-messages/remove-statements-with-invalid-format/title": "Tog bort påståenden med ogiltigt format.", @@ -143,7 +143,7 @@ "warnings-messages/no-unit-provided/body": "Värden som {example_value} på {example_item_entity} förväntas ha enhet.", "warnings-messages/invalid-entity-type/title": "{property_entity} används i objekt", "warnings-messages/invalid-entity-type/body": "Användning av {property_entity} i objekt som {example_entity} är icke giltigt.", - "wikidata-schema/copy-reference": "kopiera", - "wikidata-schema/paste-reference": "klistra referens", - "wikidata-extension/export-wikidata-schema": "Exportera Wikidata-schema" + "wikibase-schema/copy-reference": "kopiera", + "wikibase-schema/paste-reference": "klistra referens", + "wikibase-extension/export-wikibase-schema": "Exportera Wikidata-schema" } diff --git a/extensions/wikidata/module/scripts/ajv.min.js b/extensions/wikidata/module/scripts/ajv.min.js new file mode 100644 index 000000000..087c4b714 --- /dev/null +++ b/extensions/wikidata/module/scripts/ajv.min.js @@ -0,0 +1,3 @@ +/* ajv 6.12.3: Another JSON Schema Validator */ +!function(e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).Ajv=e()}(function(){return function o(i,n,l){function c(r,e){if(!n[r]){if(!i[r]){var t="function"==typeof require&&require;if(!e&&t)return t(r,!0);if(u)return u(r,!0);var a=new Error("Cannot find module '"+r+"'");throw a.code="MODULE_NOT_FOUND",a}var s=n[r]={exports:{}};i[r][0].call(s.exports,function(e){return c(i[r][1][e]||e)},s,s.exports,o,i,n,l)}return n[r].exports}for(var u="function"==typeof require&&require,e=0;e%\\^`{|}]|%[0-9a-f]{2})|\{[+#./;?&=,!@|]?(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?(?:,(?:[a-z0-9_]|%[0-9a-f]{2})+(?::[1-9][0-9]{0,3}|\*)?)*\})*$/i,u=/^(?:(?:http[s\u017F]?|ftp):\/\/)(?:(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+(?::(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*)?@)?(?:(?!10(?:\.[0-9]{1,3}){3})(?!127(?:\.[0-9]{1,3}){3})(?!169\.254(?:\.[0-9]{1,3}){2})(?!192\.168(?:\.[0-9]{1,3}){2})(?!172\.(?:1[6-9]|2[0-9]|3[01])(?:\.[0-9]{1,3}){2})(?:[1-9][0-9]?|1[0-9][0-9]|2[01][0-9]|22[0-3])(?:\.(?:1?[0-9]{1,2}|2[0-4][0-9]|25[0-5])){2}(?:\.(?:[1-9][0-9]?|1[0-9][0-9]|2[0-4][0-9]|25[0-4]))|(?:(?:(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+-?)*(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+)(?:\.(?:(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+-?)*(?:[0-9KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])+)*(?:\.(?:(?:[KSa-z\xA1-\uD7FF\uE000-\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]){2,})))(?::[0-9]{2,5})?(?:\/(?:[\0-\x08\x0E-\x1F!-\x9F\xA1-\u167F\u1681-\u1FFF\u200B-\u2027\u202A-\u202E\u2030-\u205E\u2060-\u2FFF\u3001-\uD7FF\uE000-\uFEFE\uFF00-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])*)?$/i,h=/^(?:urn:uuid:)?[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/i,d=/^(?:\/(?:[^~/]|~0|~1)*)*$/,p=/^#(?:\/(?:[a-z0-9_\-.!$&'()*+,;:=@]|%[0-9a-f]{2}|~0|~1)*)*$/i,f=/^(?:0|[1-9][0-9]*)(?:#|(?:\/(?:[^~/]|~0|~1)*)*)$/;function m(e){return a.copy(m[e="full"==e?"full":"fast"])}function v(e){var r=e.match(o);if(!r)return!1;var t,a=+r[2],s=+r[3];return 1<=a&&a<=12&&1<=s&&s<=(2!=a||((t=+r[1])%4!=0||t%100==0&&t%400!=0)?i[a]:29)}function y(e,r){var t=e.match(n);if(!t)return!1;var a=t[1],s=t[2],o=t[3];return(a<=23&&s<=59&&o<=59||23==a&&59==s&&60==o)&&(!r||t[5])}(r.exports=m).fast={date:/^\d\d\d\d-[0-1]\d-[0-3]\d$/,time:/^(?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)?$/i,"date-time":/^\d\d\d\d-[0-1]\d-[0-3]\d[t\s](?:[0-2]\d:[0-5]\d:[0-5]\d|23:59:60)(?:\.\d+)?(?:z|[+-]\d\d(?::?\d\d)?)$/i,uri:/^(?:[a-z][a-z0-9+-.]*:)(?:\/?\/)?[^\s]*$/i,"uri-reference":/^(?:(?:[a-z][a-z0-9+-.]*:)?\/?\/)?(?:[^\\\s#][^\s#]*)?(?:#[^\\\s]*)?$/i,"uri-template":c,url:u,email:/^[a-z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/i,hostname:s,ipv4:/^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/,ipv6:/^\s*(?:(?:(?:[0-9a-f]{1,4}:){7}(?:[0-9a-f]{1,4}|:))|(?:(?:[0-9a-f]{1,4}:){6}(?::[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){5}(?:(?:(?::[0-9a-f]{1,4}){1,2})|:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){4}(?:(?:(?::[0-9a-f]{1,4}){1,3})|(?:(?::[0-9a-f]{1,4})?:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){3}(?:(?:(?::[0-9a-f]{1,4}){1,4})|(?:(?::[0-9a-f]{1,4}){0,2}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){2}(?:(?:(?::[0-9a-f]{1,4}){1,5})|(?:(?::[0-9a-f]{1,4}){0,3}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){1}(?:(?:(?::[0-9a-f]{1,4}){1,6})|(?:(?::[0-9a-f]{1,4}){0,4}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?::(?:(?:(?::[0-9a-f]{1,4}){1,7})|(?:(?::[0-9a-f]{1,4}){0,5}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(?:%.+)?\s*$/i,regex:w,uuid:h,"json-pointer":d,"json-pointer-uri-fragment":p,"relative-json-pointer":f},m.full={date:v,time:y,"date-time":function(e){var r=e.split(g);return 2==r.length&&v(r[0])&&y(r[1],!0)},uri:function(e){return P.test(e)&&l.test(e)},"uri-reference":/^(?:[a-z][a-z0-9+\-.]*:)?(?:\/?\/(?:(?:[a-z0-9\-._~!$&'()*+,;=:]|%[0-9a-f]{2})*@)?(?:\[(?:(?:(?:(?:[0-9a-f]{1,4}:){6}|::(?:[0-9a-f]{1,4}:){5}|(?:[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){4}|(?:(?:[0-9a-f]{1,4}:){0,1}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){3}|(?:(?:[0-9a-f]{1,4}:){0,2}[0-9a-f]{1,4})?::(?:[0-9a-f]{1,4}:){2}|(?:(?:[0-9a-f]{1,4}:){0,3}[0-9a-f]{1,4})?::[0-9a-f]{1,4}:|(?:(?:[0-9a-f]{1,4}:){0,4}[0-9a-f]{1,4})?::)(?:[0-9a-f]{1,4}:[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?))|(?:(?:[0-9a-f]{1,4}:){0,5}[0-9a-f]{1,4})?::[0-9a-f]{1,4}|(?:(?:[0-9a-f]{1,4}:){0,6}[0-9a-f]{1,4})?::)|[Vv][0-9a-f]+\.[a-z0-9\-._~!$&'()*+,;=:]+)\]|(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)|(?:[a-z0-9\-._~!$&'"()*+,;=]|%[0-9a-f]{2})*)(?::\d*)?(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*|\/(?:(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?|(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})+(?:\/(?:[a-z0-9\-._~!$&'"()*+,;=:@]|%[0-9a-f]{2})*)*)?(?:\?(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?(?:#(?:[a-z0-9\-._~!$&'"()*+,;=:@/?]|%[0-9a-f]{2})*)?$/i,"uri-template":c,url:u,email:/^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i,hostname:s,ipv4:/^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/,ipv6:/^\s*(?:(?:(?:[0-9a-f]{1,4}:){7}(?:[0-9a-f]{1,4}|:))|(?:(?:[0-9a-f]{1,4}:){6}(?::[0-9a-f]{1,4}|(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){5}(?:(?:(?::[0-9a-f]{1,4}){1,2})|:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(?:(?:[0-9a-f]{1,4}:){4}(?:(?:(?::[0-9a-f]{1,4}){1,3})|(?:(?::[0-9a-f]{1,4})?:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){3}(?:(?:(?::[0-9a-f]{1,4}){1,4})|(?:(?::[0-9a-f]{1,4}){0,2}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){2}(?:(?:(?::[0-9a-f]{1,4}){1,5})|(?:(?::[0-9a-f]{1,4}){0,3}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?:(?:[0-9a-f]{1,4}:){1}(?:(?:(?::[0-9a-f]{1,4}){1,6})|(?:(?::[0-9a-f]{1,4}){0,4}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(?::(?:(?:(?::[0-9a-f]{1,4}){1,7})|(?:(?::[0-9a-f]{1,4}){0,5}:(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(?:\.(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(?:%.+)?\s*$/i,regex:w,uuid:h,"json-pointer":d,"json-pointer-uri-fragment":p,"relative-json-pointer":f};var g=/t|\s/i;var P=/\/|:/;var E=/[^\\]\\Z/;function w(e){if(E.test(e))return!1;try{return new RegExp(e),!0}catch(e){return!1}}},{"./util":10}],5:[function(e,r,t){"use strict";var R=e("./resolve"),$=e("./util"),j=e("./error_classes"),D=e("fast-json-stable-stringify"),O=e("../dotjs/validate"),I=$.ucs2length,A=e("fast-deep-equal"),k=j.Validation;function C(e,c,u,r){var d=this,p=this._opts,h=[void 0],f={},l=[],t={},m=[],a={},v=[],s=function(e,r,t){var a=L.call(this,e,r,t);return 0<=a?{index:a,compiling:!0}:{index:a=this._compilations.length,compiling:!(this._compilations[a]={schema:e,root:r,baseId:t})}}.call(this,e,c=c||{schema:e,refVal:h,refs:f},r),o=this._compilations[s.index];if(s.compiling)return o.callValidate=P;var y=this._formats,g=this.RULES;try{var i=E(e,c,u,r);o.validate=i;var n=o.callValidate;return n&&(n.schema=i.schema,n.errors=null,n.refs=i.refs,n.refVal=i.refVal,n.root=i.root,n.$async=i.$async,p.sourceCode&&(n.source=i.source)),i}finally{(function(e,r,t){var a=L.call(this,e,r,t);0<=a&&this._compilations.splice(a,1)}).call(this,e,c,r)}function P(){var e=o.validate,r=e.apply(this,arguments);return P.errors=e.errors,r}function E(e,r,t,a){var s=!r||r&&r.schema==e;if(r.schema!=c.schema)return C.call(d,e,r,t,a);var o=!0===e.$async,i=O({isTop:!0,schema:e,isRoot:s,baseId:a,root:r,schemaPath:"",errSchemaPath:"#",errorPath:'""',MissingRefError:j.MissingRef,RULES:g,validate:O,util:$,resolve:R,resolveRef:w,usePattern:_,useDefault:F,useCustomRule:x,opts:p,formats:y,logger:d.logger,self:d}),i=Q(h,T)+Q(l,N)+Q(m,z)+Q(v,q)+i;p.processCode&&(i=p.processCode(i,e));try{var n=new Function("self","RULES","formats","root","refVal","defaults","customRules","equal","ucs2length","ValidationError",i)(d,g,y,c,h,m,v,A,I,k);h[0]=n}catch(e){throw d.logger.error("Error compiling schema, function code:",i),e}return n.schema=e,n.errors=null,n.refs=f,n.refVal=h,n.root=s?n:r,o&&(n.$async=!0),!0===p.sourceCode&&(n.source={code:i,patterns:l,defaults:m}),n}function w(e,r,t){r=R.url(e,r);var a,s,o=f[r];if(void 0!==o)return S(a=h[o],s="refVal["+o+"]");if(!t&&c.refs){var i=c.refs[r];if(void 0!==i)return S(a=c.refVal[i],s=b(r,a))}s=b(r);var n,l=R.call(d,E,c,r);if(void 0!==l||(n=u&&u[r])&&(l=R.inlineRef(n,p.inlineRefs)?n:C.call(d,n,c,u,e)),void 0!==l)return S(h[f[r]]=l,s);delete f[r]}function b(e,r){var t=h.length;return h[t]=r,"refVal"+(f[e]=t)}function S(e,r){return"object"==typeof e||"boolean"==typeof e?{code:r,schema:e,inline:!0}:{code:r,$async:e&&!!e.$async}}function _(e){var r=t[e];return void 0===r&&(r=t[e]=l.length,l[r]=e),"pattern"+r}function F(e){switch(typeof e){case"boolean":case"number":return""+e;case"string":return $.toQuotedString(e);case"object":if(null===e)return"null";var r=D(e),t=a[r];return void 0===t&&(t=a[r]=m.length,m[t]=e),"default"+t}}function x(e,r,t,a){if(!1!==d._opts.validateSchema){var s=e.definition.dependencies;if(s&&!s.every(function(e){return Object.prototype.hasOwnProperty.call(t,e)}))throw new Error("parent schema must have all required keywords: "+s.join(","));var o=e.definition.validateSchema;if(o)if(!o(r)){var i="keyword schema is invalid: "+d.errorsText(o.errors);if("log"!=d._opts.validateSchema)throw new Error(i);d.logger.error(i)}}var n,l=e.definition.compile,c=e.definition.inline,u=e.definition.macro;if(l)n=l.call(d,r,t,a);else if(u)n=u.call(d,r,t,a),!1!==p.validateSchema&&d.validateSchema(n,!0);else if(c)n=c.call(d,a,e.keyword,r,t);else if(!(n=e.definition.validate))return;if(void 0===n)throw new Error('custom keyword "'+e.keyword+'"failed to compile');var h=v.length;return{code:"customRule"+h,validate:v[h]=n}}}function L(e,r,t){for(var a=0;a",_=P?">":"<",F=void 0;if(!y&&"number"!=typeof d&&void 0!==d)throw new Error(r+" must be number");if(!b&&void 0!==w&&"number"!=typeof w&&"boolean"!=typeof w)throw new Error(E+" must be number or boolean");b?(o="exclIsNumber"+u,i="' + "+(n="op"+u)+" + '",c+=" var schemaExcl"+u+" = "+(t=e.util.getData(w.$data,h,e.dataPathArr))+"; ",F=E,(l=l||[]).push(c+=" var "+(a="exclusive"+u)+"; var "+(s="exclType"+u)+" = typeof "+(t="schemaExcl"+u)+"; if ("+s+" != 'boolean' && "+s+" != 'undefined' && "+s+" != 'number') { "),c="",!1!==e.createErrors?(c+=" { keyword: '"+(F||"_exclusiveLimit")+"' , dataPath: (dataPath || '') + "+e.errorPath+" , schemaPath: "+e.util.toQuotedString(f)+" , params: {} ",!1!==e.opts.messages&&(c+=" , message: '"+E+" should be boolean' "),e.opts.verbose&&(c+=" , schema: validate.schema"+p+" , parentSchema: validate.schema"+e.schemaPath+" , data: "+v+" "),c+=" } "):c+=" {} ",x=c,c=l.pop(),c+=!e.compositeRule&&m?e.async?" throw new ValidationError(["+x+"]); ":" validate.errors = ["+x+"]; return false; ":" var err = "+x+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",c+=" } else if ( ",y&&(c+=" ("+g+" !== undefined && typeof "+g+" != 'number') || "),c+=" "+s+" == 'number' ? ( ("+a+" = "+g+" === undefined || "+t+" "+S+"= "+g+") ? "+v+" "+_+"= "+t+" : "+v+" "+_+" "+g+" ) : ( ("+a+" = "+t+" === true) ? "+v+" "+_+"= "+g+" : "+v+" "+_+" "+g+" ) || "+v+" !== "+v+") { var op"+u+" = "+a+" ? '"+S+"' : '"+S+"='; ",void 0===d&&(f=e.errSchemaPath+"/"+(F=E),g=t,y=b)):(i=S,(o="number"==typeof w)&&y?(n="'"+i+"'",c+=" if ( ",y&&(c+=" ("+g+" !== undefined && typeof "+g+" != 'number') || "),c+=" ( "+g+" === undefined || "+w+" "+S+"= "+g+" ? "+v+" "+_+"= "+w+" : "+v+" "+_+" "+g+" ) || "+v+" !== "+v+") { "):(o&&void 0===d?(a=!0,f=e.errSchemaPath+"/"+(F=E),g=w,_+="="):(o&&(g=Math[P?"min":"max"](w,d)),w===(!o||g)?(a=!0,f=e.errSchemaPath+"/"+(F=E),_+="="):(a=!1,i+="=")),n="'"+i+"'",c+=" if ( ",y&&(c+=" ("+g+" !== undefined && typeof "+g+" != 'number') || "),c+=" "+v+" "+_+" "+g+" || "+v+" !== "+v+") { ")),F=F||r,(l=l||[]).push(c),c="",!1!==e.createErrors?(c+=" { keyword: '"+(F||"_limit")+"' , dataPath: (dataPath || '') + "+e.errorPath+" , schemaPath: "+e.util.toQuotedString(f)+" , params: { comparison: "+n+", limit: "+g+", exclusive: "+a+" } ",!1!==e.opts.messages&&(c+=" , message: 'should be "+i+" ",c+=y?"' + "+g:g+"'"),e.opts.verbose&&(c+=" , schema: ",c+=y?"validate.schema"+p:""+d,c+=" , parentSchema: validate.schema"+e.schemaPath+" , data: "+v+" "),c+=" } "):c+=" {} ";var x=c;return c=l.pop(),c+=!e.compositeRule&&m?e.async?" throw new ValidationError(["+x+"]); ":" validate.errors = ["+x+"]; return false; ":" var err = "+x+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",c+=" } ",m&&(c+=" else { "),c}},{}],14:[function(e,r,t){"use strict";r.exports=function(e,r){var t=" ",a=e.level,s=e.dataLevel,o=e.schema[r],i=e.schemaPath+e.util.getProperty(r),n=e.errSchemaPath+"/"+r,l=!e.opts.allErrors,c="data"+(s||""),u=e.opts.$data&&o&&o.$data,h=u?(t+=" var schema"+a+" = "+e.util.getData(o.$data,s,e.dataPathArr)+"; ","schema"+a):o;if(!u&&"number"!=typeof o)throw new Error(r+" must be number");t+="if ( ",u&&(t+=" ("+h+" !== undefined && typeof "+h+" != 'number') || ");var d=r,p=p||[];p.push(t+=" "+c+".length "+("maxItems"==r?">":"<")+" "+h+") { "),t="",!1!==e.createErrors?(t+=" { keyword: '"+(d||"_limitItems")+"' , dataPath: (dataPath || '') + "+e.errorPath+" , schemaPath: "+e.util.toQuotedString(n)+" , params: { limit: "+h+" } ",!1!==e.opts.messages&&(t+=" , message: 'should NOT have ",t+="maxItems"==r?"more":"fewer",t+=" than ",t+=u?"' + "+h+" + '":""+o,t+=" items' "),e.opts.verbose&&(t+=" , schema: ",t+=u?"validate.schema"+i:""+o,t+=" , parentSchema: validate.schema"+e.schemaPath+" , data: "+c+" "),t+=" } "):t+=" {} ";var f=t,t=p.pop();return t+=!e.compositeRule&&l?e.async?" throw new ValidationError(["+f+"]); ":" validate.errors = ["+f+"]; return false; ":" var err = "+f+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",t+="} ",l&&(t+=" else { "),t}},{}],15:[function(e,r,t){"use strict";r.exports=function(e,r){var t=" ",a=e.level,s=e.dataLevel,o=e.schema[r],i=e.schemaPath+e.util.getProperty(r),n=e.errSchemaPath+"/"+r,l=!e.opts.allErrors,c="data"+(s||""),u=e.opts.$data&&o&&o.$data,h=u?(t+=" var schema"+a+" = "+e.util.getData(o.$data,s,e.dataPathArr)+"; ","schema"+a):o;if(!u&&"number"!=typeof o)throw new Error(r+" must be number");t+="if ( ",u&&(t+=" ("+h+" !== undefined && typeof "+h+" != 'number') || "),t+=!1===e.opts.unicode?" "+c+".length ":" ucs2length("+c+") ";var d=r,p=p||[];p.push(t+=" "+("maxLength"==r?">":"<")+" "+h+") { "),t="",!1!==e.createErrors?(t+=" { keyword: '"+(d||"_limitLength")+"' , dataPath: (dataPath || '') + "+e.errorPath+" , schemaPath: "+e.util.toQuotedString(n)+" , params: { limit: "+h+" } ",!1!==e.opts.messages&&(t+=" , message: 'should NOT be ",t+="maxLength"==r?"longer":"shorter",t+=" than ",t+=u?"' + "+h+" + '":""+o,t+=" characters' "),e.opts.verbose&&(t+=" , schema: ",t+=u?"validate.schema"+i:""+o,t+=" , parentSchema: validate.schema"+e.schemaPath+" , data: "+c+" "),t+=" } "):t+=" {} ";var f=t,t=p.pop();return t+=!e.compositeRule&&l?e.async?" throw new ValidationError(["+f+"]); ":" validate.errors = ["+f+"]; return false; ":" var err = "+f+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",t+="} ",l&&(t+=" else { "),t}},{}],16:[function(e,r,t){"use strict";r.exports=function(e,r){var t=" ",a=e.level,s=e.dataLevel,o=e.schema[r],i=e.schemaPath+e.util.getProperty(r),n=e.errSchemaPath+"/"+r,l=!e.opts.allErrors,c="data"+(s||""),u=e.opts.$data&&o&&o.$data,h=u?(t+=" var schema"+a+" = "+e.util.getData(o.$data,s,e.dataPathArr)+"; ","schema"+a):o;if(!u&&"number"!=typeof o)throw new Error(r+" must be number");t+="if ( ",u&&(t+=" ("+h+" !== undefined && typeof "+h+" != 'number') || ");var d=r,p=p||[];p.push(t+=" Object.keys("+c+").length "+("maxProperties"==r?">":"<")+" "+h+") { "),t="",!1!==e.createErrors?(t+=" { keyword: '"+(d||"_limitProperties")+"' , dataPath: (dataPath || '') + "+e.errorPath+" , schemaPath: "+e.util.toQuotedString(n)+" , params: { limit: "+h+" } ",!1!==e.opts.messages&&(t+=" , message: 'should NOT have ",t+="maxProperties"==r?"more":"fewer",t+=" than ",t+=u?"' + "+h+" + '":""+o,t+=" properties' "),e.opts.verbose&&(t+=" , schema: ",t+=u?"validate.schema"+i:""+o,t+=" , parentSchema: validate.schema"+e.schemaPath+" , data: "+c+" "),t+=" } "):t+=" {} ";var f=t,t=p.pop();return t+=!e.compositeRule&&l?e.async?" throw new ValidationError(["+f+"]); ":" validate.errors = ["+f+"]; return false; ":" var err = "+f+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",t+="} ",l&&(t+=" else { "),t}},{}],17:[function(e,r,t){"use strict";r.exports=function(e,r){var t=" ",a=e.schema[r],s=e.schemaPath+e.util.getProperty(r),o=e.errSchemaPath+"/"+r,i=!e.opts.allErrors,n=e.util.copy(e),l="";n.level++;var c="valid"+n.level,u=n.baseId,h=!0,d=a;if(d)for(var p,f=-1,m=d.length-1;f "+_+") { ",x=c+"["+_+"]",d.schema=$,d.schemaPath=i+"["+_+"]",d.errSchemaPath=n+"/"+_,d.errorPath=e.util.getPathExpr(e.errorPath,_,e.opts.jsonPointers,!0),d.dataPathArr[v]=_,R=e.validate(d),d.baseId=g,e.util.varOccurences(R,y)<2?t+=" "+e.util.varReplace(R,y,x)+" ":t+=" var "+y+" = "+x+"; "+R+" ",t+=" } ",l&&(t+=" if ("+f+") { ",p+="}"))}"object"==typeof b&&(e.opts.strictKeywords?"object"==typeof b&&0 "+o.length+") { for (var "+m+" = "+o.length+"; "+m+" < "+c+".length; "+m+"++) { ",d.errorPath=e.util.getPathExpr(e.errorPath,m,e.opts.jsonPointers,!0),x=c+"["+m+"]",d.dataPathArr[v]=m,R=e.validate(d),d.baseId=g,e.util.varOccurences(R,y)<2?t+=" "+e.util.varReplace(R,y,x)+" ":t+=" var "+y+" = "+x+"; "+R+" ",l&&(t+=" if (!"+f+") break; "),t+=" } } ",l&&(t+=" if ("+f+") { ",p+="}"))}else{(e.opts.strictKeywords?"object"==typeof o&&0 1e-"+e.opts.multipleOfPrecision+" ":" division"+a+" !== parseInt(division"+a+") ",t+=" ) ",u&&(t+=" ) ");var d=d||[];d.push(t+=" ) { "),t="",!1!==e.createErrors?(t+=" { keyword: 'multipleOf' , dataPath: (dataPath || '') + "+e.errorPath+" , schemaPath: "+e.util.toQuotedString(n)+" , params: { multipleOf: "+h+" } ",!1!==e.opts.messages&&(t+=" , message: 'should be multiple of ",t+=u?"' + "+h:h+"'"),e.opts.verbose&&(t+=" , schema: ",t+=u?"validate.schema"+i:""+o,t+=" , parentSchema: validate.schema"+e.schemaPath+" , data: "+c+" "),t+=" } "):t+=" {} ";var p=t,t=d.pop();return t+=!e.compositeRule&&l?e.async?" throw new ValidationError(["+p+"]); ":" validate.errors = ["+p+"]; return false; ":" var err = "+p+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",t+="} ",l&&(t+=" else { "),t}},{}],30:[function(e,r,t){"use strict";r.exports=function(e,r){var t=" ",a=e.level,s=e.dataLevel,o=e.schema[r],i=e.schemaPath+e.util.getProperty(r),n=e.errSchemaPath+"/"+r,l=!e.opts.allErrors,c="data"+(s||""),u="errs__"+a,h=e.util.copy(e);h.level++;var d,p,f,m,v="valid"+h.level;return(e.opts.strictKeywords?"object"==typeof o&&0 1) { ",t=e.schema.items&&e.schema.items.type,a=Array.isArray(t),!t||"object"==t||"array"==t||a&&(0<=t.indexOf("object")||0<=t.indexOf("array"))?i+=" outer: for (;i--;) { for (j = i; j--;) { if (equal("+p+"[i], "+p+"[j])) { "+f+" = false; break outer; } } } ":(i+=" var itemIndices = {}, item; for (;i--;) { var item = "+p+"[i]; ",i+=" if ("+e.util["checkDataType"+(a?"s":"")](t,"item",e.opts.strictNumbers,!0)+") continue; ",a&&(i+=" if (typeof item == 'string') item = '\"' + item; "),i+=" if (typeof itemIndices[item] == 'number') { "+f+" = false; j = itemIndices[item]; break; } itemIndices[item] = i; } "),i+=" } ",m&&(i+=" } "),(s=s||[]).push(i+=" if (!"+f+") { "),i="",!1!==e.createErrors?(i+=" { keyword: 'uniqueItems' , dataPath: (dataPath || '') + "+e.errorPath+" , schemaPath: "+e.util.toQuotedString(h)+" , params: { i: i, j: j } ",!1!==e.opts.messages&&(i+=" , message: 'should NOT have duplicate items (items ## ' + j + ' and ' + i + ' are identical)' "),e.opts.verbose&&(i+=" , schema: ",i+=m?"validate.schema"+u:""+c,i+=" , parentSchema: validate.schema"+e.schemaPath+" , data: "+p+" "),i+=" } "):i+=" {} ",o=i,i=s.pop(),i+=!e.compositeRule&&d?e.async?" throw new ValidationError(["+o+"]); ":" validate.errors = ["+o+"]; return false; ":" var err = "+o+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; ",i+=" } ",d&&(i+=" else { ")):d&&(i+=" if (true) { "),i}},{}],38:[function(e,r,t){"use strict";r.exports=function(a,e){var r="",t=!0===a.schema.$async,s=a.util.schemaHasRulesExcept(a.schema,a.RULES.all,"$ref"),o=a.self._getId(a.schema);if(a.opts.strictKeywords){var i=a.util.schemaUnknownRules(a.schema,a.RULES.keywords);if(i){var n="unknown keyword: "+i;if("log"!==a.opts.strictKeywords)throw new Error(n);a.logger.warn(n)}}if(a.isTop&&(r+=" var validate = ",t&&(a.async=!0,r+="async "),r+="function(data, dataPath, parentData, parentDataProperty, rootData) { 'use strict'; ",o&&(a.opts.sourceCode||a.opts.processCode)&&(r+=" /*# sourceURL="+o+" */ ")),"boolean"==typeof a.schema||!s&&!a.schema.$ref){var l=a.level,c=a.dataLevel,u=a.schema[e="false schema"],h=a.schemaPath+a.util.getProperty(e),d=a.errSchemaPath+"/"+e,p=!a.opts.allErrors,f="data"+(c||""),m="valid"+l;return!1===a.schema?(a.isTop?p=!0:r+=" var "+m+" = false; ",(H=H||[]).push(r),r="",!1!==a.createErrors?(r+=" { keyword: 'false schema' , dataPath: (dataPath || '') + "+a.errorPath+" , schemaPath: "+a.util.toQuotedString(d)+" , params: {} ",!1!==a.opts.messages&&(r+=" , message: 'boolean schema is false' "),a.opts.verbose&&(r+=" , schema: false , parentSchema: validate.schema"+a.schemaPath+" , data: "+f+" "),r+=" } "):r+=" {} ",O=r,r=H.pop(),r+=!a.compositeRule&&p?a.async?" throw new ValidationError(["+O+"]); ":" validate.errors = ["+O+"]; return false; ":" var err = "+O+"; if (vErrors === null) vErrors = [err]; else vErrors.push(err); errors++; "):r+=a.isTop?t?" return data; ":" validate.errors = null; return true; ":" var "+m+" = true; ",a.isTop&&(r+=" }; return validate; "),r}if(a.isTop){var v=a.isTop,l=a.level=0,c=a.dataLevel=0,f="data";if(a.rootId=a.resolve.fullPath(a.self._getId(a.root.schema)),a.baseId=a.baseId||a.rootId,delete a.isTop,a.dataPathArr=[void 0],void 0!==a.schema.default&&a.opts.useDefaults&&a.opts.strictDefaults){var y="default is ignored in the schema root";if("log"!==a.opts.strictDefaults)throw new Error(y);a.logger.warn(y)}r+=" var vErrors = null; ",r+=" var errors = 0; ",r+=" if (rootData === undefined) rootData = data; "}else{l=a.level,f="data"+((c=a.dataLevel)||"");if(o&&(a.baseId=a.resolve.url(a.baseId,o)),t&&!a.async)throw new Error("async schema in sync schema");r+=" var errs_"+l+" = errors;"}var g,m="valid"+l,p=!a.opts.allErrors,P="",E="",w=a.schema.type,b=Array.isArray(w);if(w&&a.opts.nullable&&!0===a.schema.nullable&&(b?-1==w.indexOf("null")&&(w=w.concat("null")):"null"!=w&&(w=[w,"null"],b=!0)),b&&1==w.length&&(w=w[0],b=!1),a.schema.$ref&&s){if("fail"==a.opts.extendRefs)throw new Error('$ref: validation keywords used in schema at path "'+a.errSchemaPath+'" (see option extendRefs)');!0!==a.opts.extendRefs&&(s=!1,a.logger.warn('$ref: keywords ignored in schema at path "'+a.errSchemaPath+'"'))}if(a.schema.$comment&&a.opts.$comment&&(r+=" "+a.RULES.all.$comment.code(a,"$comment")),w){a.opts.coerceTypes&&(g=a.util.coerceToTypes(a.opts.coerceTypes,w));var S=a.RULES.types[w];if(g||b||!0===S||S&&!G(S)){h=a.schemaPath+".type",d=a.errSchemaPath+"/type",h=a.schemaPath+".type",d=a.errSchemaPath+"/type";if(r+=" if ("+a.util[b?"checkDataTypes":"checkDataType"](w,f,a.opts.strictNumbers,!0)+") { ",g){var _="dataType"+l,F="coerced"+l;r+=" var "+_+" = typeof "+f+"; ","array"==a.opts.coerceTypes&&(r+=" if ("+_+" == 'object' && Array.isArray("+f+")) "+_+" = 'array'; "),r+=" var "+F+" = undefined; ";var x="",R=g;if(R)for(var $,j=-1,D=R.length-1;j= 0x80 (not a basic code point)","invalid-input":"Invalid input"},k=Math.floor,C=String.fromCharCode;function L(e){throw new RangeError(i[e])}function n(e,r){var t=e.split("@"),a="";return 1>1,e+=k(e/r);455k((A-a)/h))&&L("overflow"),a+=p*h;var f=d<=o?1:o+26<=d?26:d-o;if(pk(A/m)&&L("overflow"),h*=m}var v=r.length+1,o=T(a-u,v,0==u);k(a/v)>A-s&&L("overflow"),s+=k(a/v),a%=v,r.splice(a++,0,s)}return String.fromCodePoint.apply(String,r)}function c(e){var r=[],t=(e=N(e)).length,a=128,s=0,o=72,i=!0,n=!1,l=void 0;try{for(var c,u=e[Symbol.iterator]();!(i=(c=u.next()).done);i=!0){var h=c.value;h<128&&r.push(C(h))}}catch(e){n=!0,l=e}finally{try{!i&&u.return&&u.return()}finally{if(n)throw l}}var d=r.length,p=d;for(d&&r.push("-");pk((A-s)/w)&&L("overflow"),s+=(f-a)*w,a=f;var b=!0,S=!1,_=void 0;try{for(var F,x=e[Symbol.iterator]();!(b=(F=x.next()).done);b=!0){var R=F.value;if(RA&&L("overflow"),R==a){for(var $=s,j=36;;j+=36){var D=j<=o?1:o+26<=j?26:j-o;if($>6|192).toString(16).toUpperCase()+"%"+(63&r|128).toString(16).toUpperCase():"%"+(r>>12|224).toString(16).toUpperCase()+"%"+(r>>6&63|128).toString(16).toUpperCase()+"%"+(63&r|128).toString(16).toUpperCase()}function p(e){for(var r="",t=0,a=e.length;tA-Z\\x5E-\\x7E]",'[\\"\\\\]')),M=new RegExp(U,"g"),B=new RegExp("(?:(?:%[EFef][0-9A-Fa-f]%[0-9A-Fa-f][0-9A-Fa-f]%[0-9A-Fa-f][0-9A-Fa-f])|(?:%[89A-Fa-f][0-9A-Fa-f]%[0-9A-Fa-f][0-9A-Fa-f])|(?:%[0-9A-Fa-f][0-9A-Fa-f]))","g"),G=new RegExp(J("[^]","[A-Za-z0-9\\!\\$\\%\\'\\*\\+\\-\\^\\_\\`\\{\\|\\}\\~]","[\\.]",'[\\"]',K),"g"),Y=new RegExp(J("[^]",U,"[\\!\\$\\'\\(\\)\\*\\+\\,\\;\\:\\@]"),"g"),W=Y;function X(e){var r=p(e);return r.match(M)?r:e}var ee={scheme:"mailto",parse:function(e,r){var t=e,a=t.to=t.path?t.path.split(","):[];if(t.path=void 0,t.query){for(var s=!1,o={},i=t.query.split("&"),n=0,l=i.length;n +

+
+

+
+

+
+
+
+

+ +

+
+
+ + diff --git a/extensions/wikidata/module/scripts/dialogs/import-schema-dialog.html b/extensions/wikidata/module/scripts/dialogs/import-schema-dialog.html index 50d01f699..04dd9b121 100644 --- a/extensions/wikidata/module/scripts/dialogs/import-schema-dialog.html +++ b/extensions/wikidata/module/scripts/dialogs/import-schema-dialog.html @@ -1,13 +1,13 @@
-
-
-
-
-
- - -
+

+

+ +
+
+
diff --git a/extensions/wikidata/module/scripts/dialogs/import-schema-dialog.js b/extensions/wikidata/module/scripts/dialogs/import-schema-dialog.js index 5bf420619..6747e9de3 100644 --- a/extensions/wikidata/module/scripts/dialogs/import-schema-dialog.js +++ b/extensions/wikidata/module/scripts/dialogs/import-schema-dialog.js @@ -29,7 +29,7 @@ ImportSchemaDialog.launch = function() { elmts.schemaTextarea.val(evt.target.result); elmts.schemaTextarea.hide(); elmts.schemaLabel.hide(); - } + }; freader.readAsText(file); }); @@ -37,6 +37,14 @@ ImportSchemaDialog.launch = function() { var schema = null; try { schema = JSON.parse(elmts.schemaTextarea.val()); + + // If Wikibase related information is not included in the schema, + // fall back to Wikidata. + if (!schema.siteIri || !schema.mediaWikiApiEndpoint) { + schema.siteIri = WikidataManifestV1_0.wikibase.site_iri; + schema.mediaWikiApiEndpoint = WikidataManifestV1_0.mediawiki.api; + schema.editGroupsURLSchema = WikidataManifestV1_0.editgroups.url_schema; + } } catch(e) { elmts.invalidSchema.text($.i18n('import-wikibase-schema/invalid-schema')); return; @@ -51,7 +59,7 @@ ImportSchemaDialog.launch = function() { { onDone: function() { theProject.overlayModels.wikibaseSchema = schema; - SchemaAlignmentDialog._discardChanges(); + SchemaAlignment._discardChanges(); dismiss(); }, onError: function(e) { diff --git a/extensions/wikidata/module/scripts/dialogs/logged-in-dialog.html b/extensions/wikidata/module/scripts/dialogs/logged-in-dialog.html index 229fddb46..ec986415d 100644 --- a/extensions/wikidata/module/scripts/dialogs/logged-in-dialog.html +++ b/extensions/wikidata/module/scripts/dialogs/logged-in-dialog.html @@ -3,7 +3,7 @@
diff --git a/extensions/wikidata/module/scripts/dialogs/perform-edits-dialog.js b/extensions/wikidata/module/scripts/dialogs/perform-edits-dialog.js index 283747b3f..a34bd6f25 100644 --- a/extensions/wikidata/module/scripts/dialogs/perform-edits-dialog.js +++ b/extensions/wikidata/module/scripts/dialogs/perform-edits-dialog.js @@ -10,13 +10,16 @@ PerformEditsDialog.launch = function(logged_in_username, max_severity) { } this._level = DialogSystem.showDialog(frame); - - this._elmts.dialogHeader.text($.i18n('perform-wikidata-edits/dialog-header')); - this._elmts.loggedInAs.text($.i18n('perform-wikidata-edits/logged-in-as')); - this._elmts.editSummaryLabel.text($.i18n('perform-wikidata-edits/edit-summary-label')); - this._elmts.editSummary.attr('placeholder', $.i18n('perform-wikidata-edits/edit-summary-placeholder')); - this._elmts.performEditsButton.text($.i18n('perform-wikidata-edits/perform-edits')); - this._elmts.cancelButton.text($.i18n('perform-wikidata-edits/cancel')); + this._elmts.dialogHeader.text($.i18n('perform-wikibase-edits/dialog-header', WikibaseManager.getSelectedWikibaseName())); + this._elmts.loggedInAs.text($.i18n('perform-wikibase-edits/logged-in-as')); + this._elmts.editSummaryLabel.text($.i18n('perform-wikibase-edits/edit-summary-label')); + this._elmts.editSummary.attr('placeholder', $.i18n('perform-wikibase-edits/edit-summary-placeholder')); + this._elmts.maxlagLabel.text($.i18n('perform-wikibase-edits/maxlag-label')); + this._elmts.maxlag.val(WikibaseManager.getSelectedWikibaseMaxlag()); + this._elmts.maxlag.attr('placeholder', WikibaseManager.getSelectedWikibaseMaxlag()); + this._elmts.explainMaxlag.html($.i18n('perform-wikibase-edits/explain-maxlag')); + this._elmts.performEditsButton.text($.i18n('perform-wikibase-edits/perform-edits')); + this._elmts.cancelButton.text($.i18n('perform-wikibase-edits/cancel')); var hiddenIframe = $('#hiddenIframe').contents(); @@ -30,24 +33,36 @@ PerformEditsDialog.launch = function(logged_in_username, max_severity) { var formCopy = hiddenIframe.find("#wikibase-perform-edits-form"); formCopy.submit(); - if(elmts.editSummary.val().length == 0) { + if (elmts.editSummary.val().length === 0) { elmts.editSummary.focus(); - } else { - Refine.postProcess( - "wikidata", - "perform-wikibase-edits", - {}, - { summary: elmts.editSummary.val(), }, - { includeEngine: true, cellsChanged: true, columnStatsChanged: true }, - { onDone: function() { dismiss(); } } - ); + return; } + + if (elmts.maxlag.val().length === 0) { + elmts.maxlag.val(WikibaseManager.getSelectedWikibaseMaxlag()); + } + + // validate maxlag + if (!/^\+?[1-9]\d*$/.test(elmts.maxlag.val())) { + elmts.maxlag.focus(); + alert($.i18n('perform-wikibase-edits/maxlag-validation')); + return; + } + + Refine.postProcess( + "wikidata", + "perform-wikibase-edits", + {}, + { summary: elmts.editSummary.val(), maxlag: elmts.maxlag.val() }, + { includeEngine: true, cellsChanged: true, columnStatsChanged: true }, + { onDone: function() { dismiss(); } } + ); }; - + elmts.loggedInUsername .text(logged_in_username) - .attr('href','https://www.wikidata.org/wiki/User:'+logged_in_username); - + .attr('href', WikibaseManager.getSelectedWikibaseRoot() + 'User:' + logged_in_username); + frame.find('.cancel-button').click(function() { dismiss(); }); @@ -69,9 +84,11 @@ PerformEditsDialog.launch = function(logged_in_username, max_severity) { }; PerformEditsDialog.updateEditCount = function(edit_count) { - this._elmts.reviewYourEdits.html( - $.i18n('perform-wikidata-edits/review-your-edits') - .replace('{nb_edits}', edit_count)); + this._elmts.reviewYourEdits.html($.i18n('perform-wikibase-edits/review-your-edits', + edit_count, + WikibaseManager.getSelectedWikibaseMainPage(), + WikibaseManager.getSelectedWikibaseName(), + )); }; PerformEditsDialog._updateWarnings = function(data) { @@ -83,32 +100,30 @@ PerformEditsDialog._updateWarnings = function(data) { PerformEditsDialog.updateEditCount(data.edit_count); var table = $('
').appendTo(mainDiv); - for (var i = 0; i != warnings.length; i++) { + for (var i = 0; i < warnings.length; i++) { var rendered = WarningsRenderer._renderWarning(warnings[i]); rendered.appendTo(table); - } + } }; PerformEditsDialog.checkAndLaunch = function () { - var self = this; this.frame = $(DOM.loadHTML("wikidata", "scripts/dialogs/perform-edits-dialog.html")); this._elmts = DOM.bind(this.frame); this.missingSchema = false; var onSaved = function () { ManageAccountDialog.ensureLoggedIn(function (logged_in_username) { - var discardWaiter = DialogSystem.showBusy($.i18n('perform-wikidata-edits/analyzing-edits')); + var discardWaiter = DialogSystem.showBusy($.i18n('perform-wikibase-edits/analyzing-edits')); Refine.postCSRF( "command/wikidata/preview-wikibase-schema?" + $.param({project: theProject.id}), - {engine: JSON.stringify(ui.browsingEngine.getJSON())}, + {manifest: JSON.stringify(WikibaseManager.getSelectedWikibase()), engine: JSON.stringify(ui.browsingEngine.getJSON())}, function (data) { discardWaiter(); - if (data['code'] != 'error') { + if (data['code'] !== 'error') { PerformEditsDialog._updateWarnings(data); PerformEditsDialog.launch(logged_in_username, data['max_severity']); } else { - SchemaAlignmentDialog.launch( - PerformEditsDialog.checkAndLaunch); + SchemaAlignment.launch(); } }, "json" @@ -117,8 +132,8 @@ PerformEditsDialog.checkAndLaunch = function () { }; - if (SchemaAlignmentDialog.isSetUp() && SchemaAlignmentDialog._hasUnsavedChanges) { - SchemaAlignmentDialog._save(onSaved); + if (SchemaAlignment.isSetUp() && SchemaAlignment._hasUnsavedChanges) { + SchemaAlignment._save(onSaved); } else { onSaved(); } diff --git a/extensions/wikidata/module/scripts/dialogs/schema-alignment-dialog.html b/extensions/wikidata/module/scripts/dialogs/schema-alignment-dialog.html deleted file mode 100644 index 7e05b6ea4..000000000 --- a/extensions/wikidata/module/scripts/dialogs/schema-alignment-dialog.html +++ /dev/null @@ -1,49 +0,0 @@ -
-
-
-
-
    -
  • -
  • -
  • -
-
-
-

-
-
-
-
-
-
-
-
-
-
-
- - -
-
- -
diff --git a/extensions/wikidata/module/scripts/dialogs/wikibase-dialog.html b/extensions/wikidata/module/scripts/dialogs/wikibase-dialog.html new file mode 100644 index 000000000..fe214782f --- /dev/null +++ b/extensions/wikidata/module/scripts/dialogs/wikibase-dialog.html @@ -0,0 +1,18 @@ +
+
+
+

+

+
+ + +
+
+
+ +
diff --git a/extensions/wikidata/module/scripts/dialogs/wikibase-dialog.js b/extensions/wikidata/module/scripts/dialogs/wikibase-dialog.js new file mode 100644 index 000000000..01ba82076 --- /dev/null +++ b/extensions/wikidata/module/scripts/dialogs/wikibase-dialog.js @@ -0,0 +1,124 @@ +const WikibaseDialog = {}; + +WikibaseDialog.launch = function () { + const frame = $(DOM.loadHTML("wikidata", "scripts/dialogs/wikibase-dialog.html")); + const elmts = this.elmts = DOM.bind(frame); + elmts.dialogHeader.text($.i18n("wikibase-management/dialog-header")); + elmts.explainSelectWikibase.text($.i18n("wikibase-management/explain-select-wikibase")); + elmts.currentSelectedWikibase.html($.i18n("wikibase-management/current-selected-wikibase", + WikibaseManager.getSelectedWikibaseMainPage(), WikibaseManager.getSelectedWikibaseName())); + elmts.closeButton.text($.i18n("wikibase-management/close")); + elmts.addButton.text($.i18n("wikibase-management/add-wikibase")); + + WikibaseDialog.populateDialog(); + + let level = DialogSystem.showDialog(frame); + + elmts.closeButton.click(function () { + DialogSystem.dismissUntil(level - 1); + }); + + elmts.addButton.click(function () { + WikibaseDialog.addWikibaseManifest(); + }); +}; + +WikibaseDialog.populateDialog = function () { + let wikibases = WikibaseManager.getAllWikibases(); + + WikibaseDialog.elmts.wikibaseList.empty(); + for (let wikibaseName in wikibases) { + if (wikibases.hasOwnProperty(wikibaseName)) { + let item = ""; + item += "" + wikibaseName + ""; + if (wikibaseName.toLowerCase() === WikibaseManager.getSelectedWikibaseName().toLowerCase()) { + item += ""; + } else { + item += ""; + } + item += ""; + WikibaseDialog.elmts.wikibaseList.append(item); + } + } +}; + +WikibaseDialog.selectWikibase = function (wikibaseName) { + if (wikibaseName !== WikibaseManager.getSelectedWikibaseName()) { + WikibaseManager.selectWikibase(wikibaseName); + WikibaseDialog.elmts.currentSelectedWikibase.html($.i18n("wikibase-management/current-selected-wikibase", + WikibaseManager.getSelectedWikibaseMainPage(), WikibaseManager.getSelectedWikibaseName())); + WikibaseDialog.populateDialog(); + SchemaAlignment.onWikibaseChange(); + } +}; + +WikibaseDialog.removeWikibase = function (e, wikibaseName) { + e.stopPropagation(); // must stop, otherwise the removed Wikibase will be selected + WikibaseManager.removeWikibase(wikibaseName); + WikibaseDialog.populateDialog(); +}; + + +WikibaseDialog.addWikibaseManifest = function () { + const frame = $(DOM.loadHTML("wikidata", "scripts/dialogs/add-wikibase-dialog.html")); + const elmts = DOM.bind(frame); + elmts.dialogHeader.text($.i18n("wikibase-addition/dialog-header")); + elmts.explainAddManifest.text($.i18n("wikibase-addition/explain-add-manifest")); + elmts.explainAddManifestViaURL.text($.i18n("wikibase-addition/explain-add-manifest-via-url")); + elmts.explainPasteManifest.html($.i18n("wikibase-addition/explain-paste-manifest")); + elmts.cancelButton.text($.i18n("wikibase-addition/cancel")); + elmts.addButton.text($.i18n("wikibase-addition/add-wikibase")); + elmts.invalidManifest.hide(); + elmts.invalidManifest.text($.i18n("wikibase-addition/invalid-manifest")); + + let level = DialogSystem.showDialog(frame); + + elmts.cancelButton.click(function () { + DialogSystem.dismissUntil(level - 1); + }); + + elmts.addButton.click(function () { + let addManifest = function (manifest) { + if (!WikibaseDialog.validateManifest(manifest)) { + return; + } + + WikibaseManager.addWikibase(manifest); + let lang = $.i18n('core-recon/wd-recon-lang'); + let reconEndpoint = manifest.reconciliation.endpoint.replace("${lang}", lang); + ReconciliationManager.getOrRegisterServiceFromUrl(reconEndpoint, function () {}, true); + DialogSystem.dismissUntil(level - 1); + WikibaseDialog.populateDialog(); + }; + + let manifestURL = $.trim(elmts.manifestURLInput.val()); + if (manifestURL.length) { + WikibaseManager.fetchManifestFromURL(manifestURL, addManifest); + } else { + try { + let manifest = JSON.parse(elmts.manifestTextarea.val()); + addManifest(manifest); + } catch (e) { + console.error(e); + elmts.invalidManifest.show(); + } + } + }); +}; + +WikibaseDialog.validateManifest = function (manifest) { + if (!WikibaseDialog.ajv) { + WikibaseDialog.ajv = new Ajv(); + WikibaseDialog.validateWikibaseManifestV1 = WikibaseDialog.ajv.compile(WikibaseManifestSchemaV1); + } + + if (WikibaseDialog.validateWikibaseManifestV1(manifest)) { + return true; + } else { + let errMsg = WikibaseDialog.ajv.errorsText(WikibaseDialog.validateWikibaseManifestV1.errors, { + dataVar: "manifest" + }); + alert(errMsg); + return false; + } +}; diff --git a/extensions/wikidata/module/scripts/issues-tab.html b/extensions/wikidata/module/scripts/issues-tab.html index 35d752b9d..515438e16 100644 --- a/extensions/wikidata/module/scripts/issues-tab.html +++ b/extensions/wikidata/module/scripts/issues-tab.html @@ -1,5 +1,5 @@
-
+

diff --git a/extensions/wikidata/module/scripts/langsuggest.js b/extensions/wikidata/module/scripts/langsuggest.js index d830fd095..a17557b3a 100644 --- a/extensions/wikidata/module/scripts/langsuggest.js +++ b/extensions/wikidata/module/scripts/langsuggest.js @@ -1,4 +1,3 @@ - /******************** * LANGUAGE SUGGEST * ********************/ @@ -13,65 +12,97 @@ // // This is the list of language codes accepted for monolingualtext values. The language codes for terms are more restrictive. // TODO: in the future, store different lists for term languages and monolingual text languages. -WIKIDATA_LANGUAGES = [ -"aa","ab","abs","ace","ady","ady-cyrl","aeb","aeb-arab","aeb-latn","af","ak","aln","als","am","an","ang","anp", -"ar","arc","arn","arq","ary","arz","as","ase","ast","atj","av","avk","awa","ay","az","azb","ba","ban","bar", -"bbc","bbc-latn","bcc","bcl","be","be-tarask","bg","bgn","bh","bho","bi","bjn","bm","bn","bo","bpy","bqi","br", -"brh","bs","btm","bto","bug","bxr","ca","cbk-zam","cdo","ce","ceb","ch","cho","chr","chy","ckb","co","cps","cr", -"crh","crh-cyrl","crh-latn","cs","csb","cu","cv","cy","da","de","de-at","de-ch","din","diq","dsb","dtp","dty", -"dv","dz","ee","egl","el","eml","en","en-ca","en-gb","eo","es","es-419","et","eu","ext","fa","ff","fi","fit", -"fj","fo","fr","frc","frp","frr","fur","fy","ga","gag","gan","gan-hans","gan-hant","gcr","gd","gl","glk","gn", -"gom","gom-deva","gom-latn","gor","got","grc","gsw","gu","gv","ha","hak","haw","he","hi","hif","hif-latn","hil", -"ho","hr","hrx","hsb","ht","hu","hy","hyw","hz","ia","id","ie","ig","ii","ik","ike-cans","ike-latn","ilo","inh", -"io","is","it","iu","ja","jam","jbo","jut","jv","ka","kaa","kab","kbd","kbd-cyrl","kbp","kea","kg","khw","ki", -"kiu","kj","kjp","kk","kk-arab","kk-cn","kk-cyrl","kk-kz","kk-latn","kk-tr","kl","km","kn","ko","ko-kp","koi", -"kr","krc","kri","krj","krl","ks","ks-arab","ks-deva","ksh","ku","ku-arab","ku-latn","kum","kv","kw","ky","la", -"lad","lb","lbe","lez","lfn","lg","li","lij","liv","lki","lmo","ln","lo","loz","lrc","lt","ltg","lus","luz","lv", -"lzh","lzz","mai","map-bms","mdf","mg","mh","mhr","mi","min","mk","ml","mn","mni","mnw","mo","mr","mrj","ms","mt", -"mus","mwl","my","myv","mzn","na","nah","nan","nap","nb","nds","nds-nl","ne","new","ng","niu","nl","nn","no","nod", -"nov","nqo","nrm","nso","nv","ny","nys","oc","olo","om","or","os","ota","pa","pag","pam","pap","pcd","pdc","pdt", -"pfl","pi","pih","pl","pms","pnb","pnt","prg","ps","pt","pt-br","qu","qug","rgn","rif","rm","rmy","rn","ro", -"roa-tara","ru","rue","rup","ruq","ruq-cyrl","ruq-latn","rw","rwr","sa","sah","sat","sc","scn","sco","sd","sdc", -"sdh","se","sei","ses","sg","sgs","sh","shi","shi-latn","shi-tfng","shn","shy-latn","si","sje","sk","skr","skr-arab", -"sl","sli","sm","sma","smj","smn","sms","sn","so","sq","sr","sr-ec","sr-el","srn","srq","ss","st","stq","sty","su", -"sv","sw","szl","ta","tay","tcy","te","tet","tg","tg-cyrl","tg-latn","th","ti","tk","tl","tly","tn","to","tpi","tr", -"tru","ts","tt","tt-cyrl","tt-latn","tum","tw","ty","tyv","tzm","udm","ug","ug-arab","ug-latn","uk","ur","uz","uz-cyrl", -"uz-latn","ve","vec","vep","vi","vls","vmf","vo","vot","vro","wa","war","wo","wuu","xal","xh","xmf","xsy","yi","yo", -"yue","za","zea","zgh","zh","zh-cn","zh-hans","zh-hant","zh-hk","zh-mo","zh-my","zh-sg","zh-tw","zu","und","mis", -"mul","zxx","abe","abq","ami","bnn","brx","chn","cnr","cop","el-cy","ett","eya","fkv","fos","fr-ca","frm","fro", -"fuf","gez","gmy","hai","haz","hbo","kjh","koy","lag","lkt","lld","mid","mnc","moe","non","nr","nxm","ood","otk", -"pjt","ppu","pwn","pyu","quc","qya","rar","shy","sia","sjd","sjk","sjn","sjt","sju","ssf","syc","tlb","trv","tzl", -"uga","umu","uun","xpu","yap","zun" ] +DEFAULT_LANGUAGES = [ + "aa", "ab", "abs", "ace", "ady", "ady-cyrl", "aeb", "aeb-arab", "aeb-latn", "af", "ak", "aln", "als", "am", "an", "ang", "anp", + "ar", "arc", "arn", "arq", "ary", "arz", "as", "ase", "ast", "atj", "av", "avk", "awa", "ay", "az", "azb", "ba", "ban", "bar", + "bbc", "bbc-latn", "bcc", "bcl", "be", "be-tarask", "bg", "bgn", "bh", "bho", "bi", "bjn", "bm", "bn", "bo", "bpy", "bqi", "br", + "brh", "bs", "btm", "bto", "bug", "bxr", "ca", "cbk-zam", "cdo", "ce", "ceb", "ch", "cho", "chr", "chy", "ckb", "co", "cps", "cr", + "crh", "crh-cyrl", "crh-latn", "cs", "csb", "cu", "cv", "cy", "da", "de", "de-at", "de-ch", "din", "diq", "dsb", "dtp", "dty", + "dv", "dz", "ee", "egl", "el", "eml", "en", "en-ca", "en-gb", "eo", "es", "es-419", "et", "eu", "ext", "fa", "ff", "fi", "fit", + "fj", "fo", "fr", "frc", "frp", "frr", "fur", "fy", "ga", "gag", "gan", "gan-hans", "gan-hant", "gcr", "gd", "gl", "glk", "gn", + "gom", "gom-deva", "gom-latn", "gor", "got", "grc", "gsw", "gu", "gv", "ha", "hak", "haw", "he", "hi", "hif", "hif-latn", "hil", + "ho", "hr", "hrx", "hsb", "ht", "hu", "hy", "hyw", "hz", "ia", "id", "ie", "ig", "ii", "ik", "ike-cans", "ike-latn", "ilo", "inh", + "io", "is", "it", "iu", "ja", "jam", "jbo", "jut", "jv", "ka", "kaa", "kab", "kbd", "kbd-cyrl", "kbp", "kea", "kg", "khw", "ki", + "kiu", "kj", "kjp", "kk", "kk-arab", "kk-cn", "kk-cyrl", "kk-kz", "kk-latn", "kk-tr", "kl", "km", "kn", "ko", "ko-kp", "koi", + "kr", "krc", "kri", "krj", "krl", "ks", "ks-arab", "ks-deva", "ksh", "ku", "ku-arab", "ku-latn", "kum", "kv", "kw", "ky", "la", + "lad", "lb", "lbe", "lez", "lfn", "lg", "li", "lij", "liv", "lki", "lmo", "ln", "lo", "loz", "lrc", "lt", "ltg", "lus", "luz", "lv", + "lzh", "lzz", "mai", "map-bms", "mdf", "mg", "mh", "mhr", "mi", "min", "mk", "ml", "mn", "mni", "mnw", "mo", "mr", "mrj", "ms", "mt", + "mus", "mwl", "my", "myv", "mzn", "na", "nah", "nan", "nap", "nb", "nds", "nds-nl", "ne", "new", "ng", "niu", "nl", "nn", "no", "nod", + "nov", "nqo", "nrm", "nso", "nv", "ny", "nys", "oc", "olo", "om", "or", "os", "ota", "pa", "pag", "pam", "pap", "pcd", "pdc", "pdt", + "pfl", "pi", "pih", "pl", "pms", "pnb", "pnt", "prg", "ps", "pt", "pt-br", "qu", "qug", "rgn", "rif", "rm", "rmy", "rn", "ro", + "roa-tara", "ru", "rue", "rup", "ruq", "ruq-cyrl", "ruq-latn", "rw", "rwr", "sa", "sah", "sat", "sc", "scn", "sco", "sd", "sdc", + "sdh", "se", "sei", "ses", "sg", "sgs", "sh", "shi", "shi-latn", "shi-tfng", "shn", "shy-latn", "si", "sje", "sk", "skr", "skr-arab", + "sl", "sli", "sm", "sma", "smj", "smn", "sms", "sn", "so", "sq", "sr", "sr-ec", "sr-el", "srn", "srq", "ss", "st", "stq", "sty", "su", + "sv", "sw", "szl", "ta", "tay", "tcy", "te", "tet", "tg", "tg-cyrl", "tg-latn", "th", "ti", "tk", "tl", "tly", "tn", "to", "tpi", "tr", + "tru", "ts", "tt", "tt-cyrl", "tt-latn", "tum", "tw", "ty", "tyv", "tzm", "udm", "ug", "ug-arab", "ug-latn", "uk", "ur", "uz", "uz-cyrl", + "uz-latn", "ve", "vec", "vep", "vi", "vls", "vmf", "vo", "vot", "vro", "wa", "war", "wo", "wuu", "xal", "xh", "xmf", "xsy", "yi", "yo", + "yue", "za", "zea", "zgh", "zh", "zh-cn", "zh-hans", "zh-hant", "zh-hk", "zh-mo", "zh-my", "zh-sg", "zh-tw", "zu", "und", "mis", + "mul", "zxx", "abe", "abq", "ami", "bnn", "brx", "chn", "cnr", "cop", "el-cy", "ett", "eya", "fkv", "fos", "fr-ca", "frm", "fro", + "fuf", "gez", "gmy", "hai", "haz", "hbo", "kjh", "koy", "lag", "lkt", "lld", "mid", "mnc", "moe", "non", "nr", "nxm", "ood", "otk", + "pjt", "ppu", "pwn", "pyu", "quc", "qya", "rar", "shy", "sia", "sjd", "sjk", "sjn", "sjt", "sju", "ssf", "syc", "tlb", "trv", "tzl", + "uga", "umu", "uun", "xpu", "yap", "zun"]; $.suggest("langsuggest", { - _init: function() { - this.api_url = "https://www.wikidata.org/w/api.php"; + _init: function () { this._status.SELECT = "Select a language from the list:"; }, - request: function(val, cursor) { + request: function (val, cursor) { var self = this; - var ajax_options = { - url: self.api_url, - data: { action: "languagesearch", - search: val, - format: "json", }, - success: function(data) { - self.response(self.convertResults(data)); - }, + $.ajax({ dataType: "jsonp", - }; - $.ajax(ajax_options); + url: WikibaseManager.getSelectedWikibaseApi(), + data: { + action: "languagesearch", + search: val, + format: "json", + }, + success: function (data) { + self.getLanguageCodes(function (languageCodes) { + var array = []; + for (var key in data.languagesearch) { + if (data.languagesearch.hasOwnProperty(key) && languageCodes.indexOf(key) !== -1) { + array.push({id: key, name: key, search_name: data.languagesearch[key]}); + } + } + self.response(array); + }); + }, + }); }, - - convertResults: function(data) { - var array = []; - for (var key in data.languagesearch) { - if (data.languagesearch.hasOwnProperty(key) && WIKIDATA_LANGUAGES.indexOf(key) != -1) { - array.push({ id: key, name: key, search_name: data.languagesearch[key] }); - } + + apiEndpointToLangCodes: {}, + + getLanguageCodes: function (callback) { + var self = this; + var selectedApi = WikibaseManager.getSelectedWikibaseApi(); + if (self.apiEndpointToLangCodes[selectedApi]) { + callback(self.apiEndpointToLangCodes[selectedApi]); + } else { + // try fetching the language codes + // action=query&meta=wbcontentlanguages&wbclcontext=monolingualtext&format=json + $.ajax({ + type: "POST", + dataType: "jsonp", + url: WikibaseManager.getSelectedWikibaseApi() + + "?action=query&meta=wbcontentlanguages&wbclcontext=monolingualtext&format=json", + success: function (data) { + var result = []; + var langs = data.query.wbcontentlanguages; + for (var lang in langs) { + if (langs.hasOwnProperty(lang)) { + result.push(langs[lang].code); + } + } + self.apiEndpointToLangCodes[selectedApi] = result; + callback(result); + }, + error: function () { + self.apiEndpointToLangCodes[selectedApi] = DEFAULT_LANGUAGES; + callback(DEFAULT_LANGUAGES); + }, + }); } - return array; }, create_item: function(data, response_data) { diff --git a/extensions/wikidata/module/scripts/menu-bar-extension.js b/extensions/wikidata/module/scripts/menu-bar-extension.js index 29e6cef8b..7861809d5 100644 --- a/extensions/wikidata/module/scripts/menu-bar-extension.js +++ b/extensions/wikidata/module/scripts/menu-bar-extension.js @@ -1,134 +1,153 @@ // Load the localization file var dictionary = {}; $.ajax({ - url : "command/core/load-language?", - type : "POST", - async : false, - data : { - module : "wikidata", + url: "command/core/load-language?", + type: "POST", + async: false, + data: { + module: "wikidata", // lang : lang - }, - success : function(data) { - dictionary = data['dictionary']; - lang = data['lang']; - } + }, + success: function (data) { + dictionary = data['dictionary']; + lang = data['lang']; + } }); $.i18n().load(dictionary, lang); - ExporterManager.MenuItems.push({}); -ExporterManager.MenuItems.push( - { - id:"performWikibaseEdits", - label: $.i18n('wikidata-extension/wikidata-edits'), - click: function() { PerformEditsDialog.checkAndLaunch(); } - }); -ExporterManager.MenuItems.push( - { - id:"exportQuickStatements", - label: $.i18n('wikidata-extension/qs-file'), - click: function() { WikibaseExporterMenuBar.checkSchemaAndExport("quickstatements"); } - }); -ExporterManager.MenuItems.push( - { - id:"exportWikibaseSchema", - label: $.i18n('wikidata-extension/wikidata-schema'), - click: function() { WikibaseExporterMenuBar.checkSchemaAndExport("wikibase-schema"); } - } -); +ExporterManager.MenuItems.push({ + id: "performWikibaseEdits", + label: $.i18n('wikibase-extension/wikibase-edits'), + click: function () { + PerformEditsDialog.checkAndLaunch(); + } +}); +ExporterManager.MenuItems.push({ + id: "exportQuickStatements", + label: $.i18n('wikibase-extension/qs-file'), + click: function () { + WikibaseExporterMenuBar.checkSchemaAndExport("quickstatements"); + } +}); +ExporterManager.MenuItems.push({ + id: "exportWikibaseSchema", + label: $.i18n('wikibase-extension/wikibase-schema'), + click: function () { + WikibaseExporterMenuBar.checkSchemaAndExport("wikibase-schema"); + } +}); WikibaseExporterMenuBar = {}; -WikibaseExporterMenuBar.exportTo = function(format) { - var targetUrl = null; - if (format ==="quickstatements") { - targetUrl = "statements.txt"; - } else { - targetUrl = "schema.json"; - } - var form = document.createElement("form"); - $(form).css("display", "none") - .attr("method", "post") - .attr("action", "command/core/export-rows/"+targetUrl) - .attr("target", "gridworks-export-"+format); - $('') - .attr("name", "engine") - .val(JSON.stringify(ui.browsingEngine.getJSON())) - .appendTo(form); - $('') - .attr("name", "project") - .val(theProject.id) - .appendTo(form); - $('') - .attr("name", "format") - .val(format) - .appendTo(form); +WikibaseExporterMenuBar.exportTo = function (format) { + var targetUrl = null; + if (format === "quickstatements") { + targetUrl = "statements.txt"; + } else { + targetUrl = "schema.json"; + } + var form = document.createElement("form"); + $(form).css("display", "none") + .attr("method", "post") + .attr("action", "command/core/export-rows/" + targetUrl) + .attr("target", "openrefine-export-" + format); + $('') + .attr("name", "engine") + .val(JSON.stringify(ui.browsingEngine.getJSON())) + .appendTo(form); + $('') + .attr("name", "project") + .val(theProject.id) + .appendTo(form); + $('') + .attr("name", "format") + .val(format) + .appendTo(form); - document.body.appendChild(form); + document.body.appendChild(form); - window.open("about:blank", "gridworks-export"); - form.submit(); + window.open("about:blank", "openrefine-export"); + form.submit(); - document.body.removeChild(form); + document.body.removeChild(form); }; -WikibaseExporterMenuBar.checkSchemaAndExport = function(format) { - var onSaved = function(callback) { - WikibaseExporterMenuBar.exportTo(format); +WikibaseExporterMenuBar.checkSchemaAndExport = function (format) { + var onSaved = function (callback) { + WikibaseExporterMenuBar.exportTo(format); }; - if (!SchemaAlignmentDialog.isSetUp()) { - SchemaAlignmentDialog.launch(null); - } else if (SchemaAlignmentDialog._hasUnsavedChanges) { - SchemaAlignmentDialog._save(onSaved); + if (!SchemaAlignment.isSetUp()) { + SchemaAlignment.launch(null); + } else if (SchemaAlignment._hasUnsavedChanges) { + SchemaAlignment._save(onSaved); } else { - onSaved(); + onSaved(); } -} +}; //extend the column header menu -$(function(){ - - ExtensionBar.MenuItems.push( - { - "id":"reconcile", - "label": $.i18n('wikidata-extension/menu-label'), - "submenu" : [ - { - id: "wikidata/edit-schema", - label: $.i18n('wikidata-extension/edit-wikidata-schema'), - click: function() { SchemaAlignmentDialog.launch(false); } - }, - { - id:"wikidata/manage-account", - label: $.i18n('wikidata-extension/manage-wikidata-account'), - click: function() { ManageAccountDialog.checkAndLaunch(); } - }, - {}, - { - id: "wikidata/import-schema", - label: $.i18n('wikidata-extension/import-wikidata-schema'), - click: function() { ImportSchemaDialog.launch(); } - }, - { - id:"wikidata/export-schema", - label: $.i18n('wikidata-extension/export-schema'), - click: function() { WikibaseExporterMenuBar.checkSchemaAndExport("wikibase-schema"); } - }, - {}, - { - id:"wikidata/perform-edits", - label: $.i18n('wikidata-extension/perform-edits-on-wikidata'), - click: function() { PerformEditsDialog.checkAndLaunch(); } - }, - { - id:"wikidata/export-qs", - label: $.i18n('wikidata-extension/export-to-qs'), - click: function() { WikibaseExporterMenuBar.checkSchemaAndExport("quickstatements"); } - }, +$(function () { - ] - } - ); + ExtensionBar.MenuItems.push( + { + "id": "reconcile", + "label": $.i18n('wikibase-extension/menu-label'), + "submenu": [ + { + id: "wikidata/select-instance", + label: $.i18n('wikibase-extension/select-wikibase-instance'), + click: function () { + WikibaseDialog.launch() + } + }, + { + id: "wikidata/edit-schema", + label: $.i18n('wikibase-extension/edit-wikibase-schema'), + click: function () { + SchemaAlignment.launch(false); + } + }, + { + id: "wikidata/manage-account", + label: $.i18n('wikibase-extension/manage-wikibase-account'), + click: function () { + ManageAccountDialog.checkAndLaunch(); + } + }, + {}, + { + id: "wikidata/import-schema", + label: $.i18n('wikibase-extension/import-wikibase-schema'), + click: function () { + ImportSchemaDialog.launch(); + } + }, + { + id: "wikidata/export-schema", + label: $.i18n('wikibase-extension/export-schema'), + click: function () { + WikibaseExporterMenuBar.checkSchemaAndExport("wikibase-schema"); + } + }, + {}, + { + id: "wikidata/perform-edits", + label: $.i18n('wikibase-extension/perform-edits-on-wikibase'), + click: function () { + PerformEditsDialog.checkAndLaunch(); + } + }, + { + id: "wikidata/export-qs", + label: $.i18n('wikibase-extension/export-to-qs'), + click: function () { + WikibaseExporterMenuBar.checkSchemaAndExport("quickstatements"); + } + } + ] + } + ); }); diff --git a/extensions/wikidata/module/scripts/preview-tab.html b/extensions/wikidata/module/scripts/preview-tab.html index 12a917323..ab5d688a0 100644 --- a/extensions/wikidata/module/scripts/preview-tab.html +++ b/extensions/wikidata/module/scripts/preview-tab.html @@ -1,6 +1,6 @@

-
+

diff --git a/extensions/wikidata/module/scripts/previewrenderer.js b/extensions/wikidata/module/scripts/previewrenderer.js index 87e5781e6..dbd64c377 100644 --- a/extensions/wikidata/module/scripts/previewrenderer.js +++ b/extensions/wikidata/module/scripts/previewrenderer.js @@ -11,10 +11,10 @@ EditRenderer.maxStatements = 25; // max number of statements per statement group // main method: takes a DOM element and a list // of edits to render there. EditRenderer.renderEdits = function(edits, container) { - for(var i = 0; i != edits.length; i++) { + for(var i = 0; i < edits.length; i++) { EditRenderer._renderItem(edits[i], container); } -} +}; /**************/ /*** ITEMS ****/ @@ -57,14 +57,14 @@ EditRenderer._renderItem = function(json, container) { // Statements if (json.addedStatementGroups && json.addedStatementGroups.length) { // $('
').addClass('wbs-statements-header') - // .text($.i18n('wikidata-schema/statements-header')).appendTo(right); + // .text($.i18n('wikibase-schema/statements-header')).appendTo(right); var statementsGroupContainer = $('
').addClass('wbs-statement-group-container') .appendTo(right); for(var i = 0; i != json.addedStatementGroups.length; i++) { EditRenderer._renderStatementGroup(json.addedStatementGroups[i], statementsGroupContainer); } } -} +}; /************************** * NAMES AND DESCRIPTIONS * @@ -80,18 +80,18 @@ EditRenderer._renderTermsList = function(termList, termType, termsContainer) { if(termList.length > this.maxTerms) { $('
').addClass('wbs-namedesc').text('...').appendTo(termsContainer); } -} +}; EditRenderer._renderTerm = function(termType, json, container) { var namedesc = $('
').addClass('wbs-namedesc').appendTo(container); var type_container = $('
').addClass('wbs-namedesc-type').appendTo(namedesc); var type_span = $('').appendTo(type_container) - .text($.i18n('wikidata-schema/'+termType)); + .text($.i18n('wikibase-schema/'+termType)); var right = $('
').addClass('wbs-right').appendTo(namedesc); var value_container = $('
').addClass('wbs-namedesc-value').appendTo(namedesc); EditRenderer._renderValue({datavalue:json,datatype:'monolingualtext'}, value_container); -} +}; /******************** * STATEMENT GROUPS * @@ -114,7 +114,7 @@ EditRenderer._renderStatementGroup = function(json, container) { .addClass('wbs-statement') .appendTo(statementContainer); } -} +}; /************** * STATEMENTS * @@ -165,7 +165,7 @@ EditRenderer._renderStatement = function(json, container) { } } EditRenderer._updateReferencesNumber(referenceContainer); -} +}; /********************************* * QUALIFIER AND REFERENCE SNAKS * @@ -180,7 +180,7 @@ EditRenderer._renderSnak = function(json, container) { var statementContainer = $('
').addClass('wbs-statement-container').appendTo(right); EditRenderer._renderEntity(json.full_property, inputContainer); EditRenderer._renderValue(json, statementContainer); -} +}; /************** * REFERENCES * @@ -206,8 +206,8 @@ EditRenderer._updateReferencesNumber = function(container) { var childrenCount = container.children().length; var statement = container.parents('.wbs-statement'); var a = statement.find('.wbs-references-toggle a').first(); - a.html(childrenCount+$.i18n('wikidata-schema/nb-references')); -} + a.html(childrenCount+$.i18n('wikibase-schema/nb-references')); +}; /******************* * VALUE RENDERING * @@ -218,7 +218,7 @@ EditRenderer.renderedValueCache = {}; EditRenderer._renderEntity = function(json, container) { var html = WarningsRenderer._renderEntity(json); $(html).appendTo(container); -} +}; EditRenderer._renderValue = function(json, container) { var input = $('').appendTo(container); @@ -245,7 +245,7 @@ EditRenderer._renderValue = function(json, container) { params.datatype = json.datatype; } $.get( - 'https://www.wikidata.org/w/api.php', + WikibaseManager.getSelectedWikibaseApi(), params, function (data) { if('result' in data) { @@ -257,6 +257,6 @@ EditRenderer._renderValue = function(json, container) { ); } } -} +}; diff --git a/extensions/wikidata/module/scripts/dialogs/schema-alignment-dialog.js b/extensions/wikidata/module/scripts/schema-alignment.js similarity index 69% rename from extensions/wikidata/module/scripts/dialogs/schema-alignment-dialog.js rename to extensions/wikidata/module/scripts/schema-alignment.js index 387c2083e..d4726c77c 100644 --- a/extensions/wikidata/module/scripts/dialogs/schema-alignment-dialog.js +++ b/extensions/wikidata/module/scripts/schema-alignment.js @@ -31,15 +31,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -var SchemaAlignment = {}; -var SchemaAlignmentDialog = {}; +var SchemaAlignment = { + _isSetUp: false +}; /** * Installs the tabs in the UI the first time the Wikidata * extension is called. */ -SchemaAlignmentDialog.setUpTabs = function() { - var self = this; +SchemaAlignment.setUpTabs = function() { + this._isSetUp = true; this._rightPanel = $('#right-panel'); this._viewPanel = $('#view-panel').addClass('main-view-panel-tab'); this._toolPanel = $('#tool-panel'); @@ -48,25 +49,27 @@ SchemaAlignmentDialog.setUpTabs = function() { .addClass('active') .attr('href', '#view-panel'); - this._schemaPanel = $('
') + // append panels + this._schemaPanel = $('
') .addClass('main-view-panel-tab') .appendTo(this._rightPanel); - this._issuesPanel = $('
') + this._issuesPanel = $('
') .addClass('main-view-panel-tab') .appendTo(this._rightPanel); - this._previewPanel = $('
') + this._previewPanel = $('
') .addClass('main-view-panel-tab') .appendTo(this._rightPanel); - + + // append tools var schemaButton = $('
') .addClass('main-view-panel-tab-header') - .attr('href', '#wikidata-schema-panel') - .text($.i18n('wikidata-schema/schema-tab-header')) + .attr('href', '#wikibase-schema-panel') + .text($.i18n('wikibase-schema/schema-tab-header')) .appendTo(this._toolPanel); var issuesButton = $('
') .addClass('main-view-panel-tab-header') - .attr('href', '#wikidata-issues-panel') - .text($.i18n('wikidata-schema/warnings-tab-header')+' ') + .attr('href', '#wikibase-issues-panel') + .text($.i18n('wikibase-schema/warnings-tab-header')+' ') .appendTo(this._toolPanel); this.issuesTabCount = $('') .addClass('schema-alignment-total-warning-count') @@ -78,8 +81,8 @@ SchemaAlignmentDialog.setUpTabs = function() { .appendTo(issuesButton); var previewButton = $('
') .addClass('main-view-panel-tab-header') - .attr('href', '#wikidata-preview-panel') - .text($.i18n('wikidata-schema/edits-preview-tab-header')) + .attr('href', '#wikibase-preview-panel') + .text($.i18n('wikibase-schema/edits-preview-tab-header')) .appendTo(this._toolPanel); this.previewSpinner = $('') .attr('src', 'images/large-spinner.gif') @@ -88,85 +91,108 @@ SchemaAlignmentDialog.setUpTabs = function() { this._unsavedIndicator = $('') .html(' *') - .attr('title', $.i18n('wikidata-schema/unsaved-changes-alt')) + .attr('title', $.i18n('wikibase-schema/unsaved-changes-alt')) .hide() .appendTo(schemaButton); $('.main-view-panel-tab-header').click(function(e) { var targetTab = $(this).attr('href'); - SchemaAlignmentDialog.switchTab(targetTab); + SchemaAlignment.switchTab(targetTab); e.preventDefault(); }); + SchemaAlignment._rerenderTabs(); +}; + +/** + * Called on tabs setup or Wikibase manifest change. + */ +SchemaAlignment._rerenderTabs = function() { + if (!SchemaAlignment._isSetUp) { + SchemaAlignment.setUpTabs(); + return; + } + /** * Init the schema tab */ + this._schemaPanel.empty(); var schemaTab = $(DOM.loadHTML("wikidata", "scripts/schema-alignment-tab.html")).appendTo(this._schemaPanel); var schemaElmts = this._schemaElmts = DOM.bind(schemaTab); - schemaElmts.dialogExplanation.text($.i18n('wikidata-schema/dialog-explanation')); - this._plusButton($.i18n('wikidata-schema/add-item-button'), schemaElmts.addItemButton); + schemaElmts.dialogExplanation.html($.i18n('wikibase-schema/dialog-explanation', + WikibaseManager.getSelectedWikibaseMainPage(), + WikibaseManager.getSelectedWikibaseName(), + WikibaseManager.getSelectedWikibaseReconEndpoint().replace("${lang}", "en"))); + this._plusButton($.i18n('wikibase-schema/add-item-button'), schemaElmts.addItemButton); schemaElmts.addItemButton.click(function(e) { - self._addItem(); - SchemaAlignmentDialog._hasChanged(); + SchemaAlignment._addItem(); + SchemaAlignment._hasChanged(); e.preventDefault(); }); schemaElmts.saveButton - .text($.i18n('wikidata-schema/save-button')) - .attr('title', $.i18n('wikidata-schema/save-schema-alt')) - .prop('disabled', true) - .addClass('disabled') - .click(function() { SchemaAlignmentDialog._save(); }); + .text($.i18n('wikibase-schema/save-button')) + .attr('title', $.i18n('wikibase-schema/save-schema-alt')) + .prop('disabled', true) + .addClass('disabled') + .click(function() { SchemaAlignment._save(); }); schemaElmts.discardButton - .text($.i18n('wikidata-schema/discard-button')) - .attr('title', $.i18n('wikidata-schema/discard-schema-changes-alt')) - .prop('disabled', true) - .addClass('disabled') - .click(function() { SchemaAlignmentDialog._discardChanges(); }); - - this._wikibasePrefix = "http://www.wikidata.org/entity/"; // hardcoded for now + .text($.i18n('wikibase-schema/discard-button')) + .attr('title', $.i18n('wikibase-schema/discard-schema-changes-alt')) + .prop('disabled', true) + .addClass('disabled') + .click(function() { SchemaAlignment._discardChanges(); }); // Init the column area this.updateColumns(); - /** * Init the issues tab */ + this._issuesPanel.empty(); var issuesTab = $(DOM.loadHTML("wikidata", "scripts/issues-tab.html")).appendTo(this._issuesPanel); var issuesElmts = this._issuesElmts = DOM.bind(issuesTab); - issuesElmts.invalidSchemaWarningIssues.text($.i18n('wikidata-schema/invalid-schema-warning-issues')); + issuesElmts.invalidSchemaWarningIssues.text($.i18n('wikibase-schema/invalid-schema-warning-issues')); /** * Init the preview tab */ + this._previewPanel.empty(); var previewTab = $(DOM.loadHTML("wikidata", "scripts/preview-tab.html")).appendTo(this._previewPanel); var previewElmts = this._previewElmts = DOM.bind(previewTab); - SchemaAlignmentDialog.updateNbEdits(0); - previewElmts.invalidSchemaWarningPreview.text($.i18n('wikidata-schema/invalid-schema-warning-preview')); - + SchemaAlignment.updateNbEdits(0); + previewElmts.invalidSchemaWarningPreview.text($.i18n('wikibase-schema/invalid-schema-warning-preview')); this._previewPanes = $(".schema-alignment-dialog-preview"); - var lang = $.i18n('core-recon/wd-recon-lang'); - var url = "https://wdreconcile.toolforge.org/"+lang+"/api"; - ReconciliationManager.getOrRegisterServiceFromUrl(url, function(service) { + var reconServiceURL = WikibaseManager.getSelectedWikibaseReconEndpoint() + .replace("${lang}", $.i18n("core-recon/wd-recon-lang")); + ReconciliationManager.getOrRegisterServiceFromUrl(reconServiceURL, function (service) { + SchemaAlignment._reconService = service; // Load the existing schema - SchemaAlignmentDialog._reconService = service; - SchemaAlignmentDialog._reset(theProject.overlayModels.wikibaseSchema); + SchemaAlignment._reset(theProject.overlayModels.wikibaseSchema); // Perform initial preview - SchemaAlignmentDialog.preview(); + SchemaAlignment.preview(); }, false); -} +}; -SchemaAlignmentDialog.updateColumns = function() { +SchemaAlignment.onWikibaseChange = function() { + SchemaAlignment._rerenderTabs(); + SchemaAlignment._save(function () { + SchemaAlignment._reset(theProject.overlayModels.wikibaseSchema); + SchemaAlignment.preview(); + }); +}; + +SchemaAlignment.updateColumns = function() { var columns = theProject.columnModel.columns; this._columnArea = $(".schema-alignment-dialog-columns-area"); this._columnArea.empty(); for (var i = 0; i < columns.length; i++) { var column = columns[i]; var reconConfig = column.reconConfig; - var cell = SchemaAlignmentDialog._createDraggableColumn(column.name, - reconConfig && reconConfig.identifierSpace === this._wikibasePrefix && column.reconStats); + // make sure the column was reconciled to the target Wikibase + var cell = SchemaAlignment._createDraggableColumn(column.name, + reconConfig && reconConfig.identifierSpace === WikibaseManager.getSelectedWikibaseSiteIri() && column.reconStats); this._columnArea.append(cell); } @@ -182,9 +208,9 @@ SchemaAlignmentDialog.updateColumns = function() { snap: ".wbs-target-input input", zIndex: 100, }); -} +}; -SchemaAlignmentDialog.switchTab = function(targetTab) { +SchemaAlignment.switchTab = function(targetTab) { $('.main-view-panel-tab').hide(); $('.main-view-panel-tab-header').removeClass('active'); $('.main-view-panel-tab-header[href="'+targetTab+'"]').addClass('active'); @@ -201,53 +227,66 @@ SchemaAlignmentDialog.switchTab = function(targetTab) { if (targetTab === "#view-panel") { ui.dataTableView.render(); } -} +}; -SchemaAlignmentDialog.isSetUp = function() { - return $('#wikidata-schema-panel').length !== 0; -} +SchemaAlignment.isSetUp = function() { + return SchemaAlignment._isSetUp; +}; -SchemaAlignmentDialog.launch = function(onDone) { - this._onDone = onDone; +SchemaAlignment.launch = function() { this._hasUnsavedChanges = false; - if (!SchemaAlignmentDialog.isSetUp()) { - SchemaAlignmentDialog.setUpTabs(); + if (!SchemaAlignment.isSetUp()) { + SchemaAlignment.setUpTabs(); } - SchemaAlignmentDialog.switchTab('#wikidata-schema-panel'); - - // this._createDialog(); -} + SchemaAlignment.switchTab('#wikibase-schema-panel'); +}; var beforeUnload = function(e) { - if (SchemaAlignmentDialog.isSetUp() && SchemaAlignmentDialog._hasUnsavedChanges === true) { - return (e = $.i18n('wikidata-schema/unsaved-warning')); + if (SchemaAlignment.isSetUp() && SchemaAlignment._hasUnsavedChanges === true) { + return $.i18n('wikibase-schema/unsaved-warning'); } }; $(window).bind('beforeunload', beforeUnload); -SchemaAlignmentDialog._reset = function(schema) { - this._originalSchema = schema || { itemDocuments: [] }; +SchemaAlignment._reset = function(schema) { + if (!schema) { + schema = {}; + } + + // fall back to Wikidata + if (!schema.siteIri) { + schema.siteIri = WikidataManifestV1_0.wikibase.site_iri; + } + if (!schema.mediaWikiApiEndpoint) { + schema.mediaWikiApiEndpoint = WikidataManifestV1_0.mediawiki.api; + } + + if (!schema.itemDocuments) { + schema.itemDocuments = []; + } + + this._originalSchema = schema; this._schema = cloneDeep(this._originalSchema); // this is what can be munched on this._copiedReference = null; $('#schema-alignment-statements-container').empty(); if (this._schema && this._schema.itemDocuments) { - for(var i = 0; i != this._schema.itemDocuments.length; i++) { + for(var i = 0; i !== this._schema.itemDocuments.length; i++) { this._addItem(this._schema.itemDocuments[i]); } } }; -SchemaAlignmentDialog._save = function(onDone) { +SchemaAlignment._save = function(onDone) { var self = this; var schema = this.getJSON(); if (schema === null) { - alert($.i18n('wikidata-schema/incomplete-schema-could-not-be-saved')); + alert($.i18n('wikibase-schema/incomplete-schema-could-not-be-saved')); } Refine.postProcess( @@ -266,18 +305,18 @@ SchemaAlignmentDialog._save = function(onDone) { if (onDone) onDone(); }, onError: function(e) { - alert($.i18n('wikidata-schema/incomplete-schema-could-not-be-saved')); + alert($.i18n('wikibase-schema/incomplete-schema-could-not-be-saved')); }, } ); }; -SchemaAlignmentDialog._discardChanges = function() { +SchemaAlignment._discardChanges = function() { this._reset(theProject.overlayModels.wikibaseSchema); this._changesCleared(); -} +}; -SchemaAlignmentDialog._changesCleared = function() { +SchemaAlignment._changesCleared = function() { this._hasUnsavedChanges = false; this._unsavedIndicator.hide(); this._schemaElmts.saveButton @@ -286,9 +325,9 @@ SchemaAlignmentDialog._changesCleared = function() { this._schemaElmts.discardButton .prop('disabled', true) .addClass('disabled'); -} +}; -SchemaAlignmentDialog._createDraggableColumn = function(name, reconciled) { +SchemaAlignment._createDraggableColumn = function(name, reconciled) { var cell = $("
").addClass('wbs-draggable-column').text(name); if (reconciled) { cell.addClass('wbs-reconciled-column'); @@ -296,30 +335,29 @@ SchemaAlignmentDialog._createDraggableColumn = function(name, reconciled) { cell.addClass('wbs-unreconciled-column'); } return cell; -} +}; -SchemaAlignmentDialog._plusButton = function(label, element) { - var plus = $('').html('+ ').appendTo(element); - var span = $('').text(label) - .appendTo(element); -} +SchemaAlignment._plusButton = function(label, element) { + $('').html('+ ').appendTo(element); + $('').text(label).appendTo(element); +}; -SchemaAlignmentDialog._makeDeleteButton = function (noText) { +SchemaAlignment._makeDeleteButton = function (noText) { var button = $('
').addClass('wbs-remove').append( $('').addClass('wbs-icon') ); if(noText === undefined) { button.append( - $('').text($.i18n('wikidata-schema/remove'))); + $('').text($.i18n('wikibase-schema/remove'))); } return button; -} +}; /**************/ /*** ITEMS ****/ /**************/ -SchemaAlignmentDialog._addItem = function(json) { +SchemaAlignment._addItem = function(json) { var subject = null; var statementGroups = null; var nameDescs = null; @@ -334,69 +372,69 @@ SchemaAlignmentDialog._addItem = function(json) { var deleteToolbar = $('
').addClass('wbs-toolbar') .attr('style', 'margin-top: 10px') .appendTo(item); - var deleteButton = SchemaAlignmentDialog._makeDeleteButton() + var deleteButton = SchemaAlignment._makeDeleteButton() .appendTo(deleteToolbar) .click(function(e) { item.remove(); - SchemaAlignmentDialog._hasChanged(); + SchemaAlignment._hasChanged(); e.preventDefault(); }); var inputContainer = $('
').addClass('wbs-item-input').appendTo(item); - SchemaAlignmentDialog._initField(inputContainer, "wikibase-item", subject); + SchemaAlignment._initField(inputContainer, "wikibase-item", subject); var right = $('
').addClass('wbs-item-contents').appendTo(item); // Terms $('').addClass('wbs-namedesc-header') - .text($.i18n('wikidata-schema/terms-header')).appendTo(right); + .text($.i18n('wikibase-schema/terms-header')).appendTo(right); $('
').addClass('wbs-namedesc-container') - .attr('data-emptyplaceholder', $.i18n('wikidata-schema/empty-terms')) + .attr('data-emptyplaceholder', $.i18n('wikibase-schema/empty-terms')) .appendTo(right); var termToolbar = $('
').addClass('wbs-toolbar').appendTo(right); var addNamedescButton = $('').addClass('wbs-add-namedesc') .click(function(e) { - SchemaAlignmentDialog._addNameDesc(item, null); + SchemaAlignment._addNameDesc(item, null); e.preventDefault(); }).appendTo(termToolbar); - SchemaAlignmentDialog._plusButton( - $.i18n('wikidata-schema/add-term'), addNamedescButton); + SchemaAlignment._plusButton( + $.i18n('wikibase-schema/add-term'), addNamedescButton); // Clear the float $('
').attr('style', 'clear: right').appendTo(right); // Statements $('
').addClass('wbs-statements-header') - .text($.i18n('wikidata-schema/statements-header')).appendTo(right); + .text($.i18n('wikibase-schema/statements-header')).appendTo(right); $('
').addClass('wbs-statement-group-container') - .attr('data-emptyplaceholder', $.i18n('wikidata-schema/empty-statements')) + .attr('data-emptyplaceholder', $.i18n('wikibase-schema/empty-statements')) .appendTo(right); var statementToolbar = $('
').addClass('wbs-toolbar').appendTo(right); var addStatementButton = $('').addClass('wbs-add-statement-group') .click(function(e) { - SchemaAlignmentDialog._addStatementGroup(item, null); + SchemaAlignment._addStatementGroup(item, null); e.preventDefault(); }).appendTo(statementToolbar); - SchemaAlignmentDialog._plusButton( - $.i18n('wikidata-schema/add-statement'), addStatementButton); + SchemaAlignment._plusButton( + $.i18n('wikibase-schema/add-statement'), addStatementButton); if (statementGroups) { for(var i = 0; i != statementGroups.length; i++) { - SchemaAlignmentDialog._addStatementGroup(item, statementGroups[i]); + SchemaAlignment._addStatementGroup(item, statementGroups[i]); } } if (nameDescs) { for(var i = 0; i != nameDescs.length; i++) { - SchemaAlignmentDialog._addNameDesc(item, nameDescs[i]); + SchemaAlignment._addNameDesc(item, nameDescs[i]); } } -} +}; -SchemaAlignmentDialog._itemToJSON = function (item) { +SchemaAlignment._itemToJSON = function (item) { var statementGroupLst = new Array(); var statementsDom = item.find('.wbs-statement-group'); statementsDom.each(function () { - var statementGroupJSON = SchemaAlignmentDialog._statementGroupToJSON($(this)); + var statementGroupJSON = SchemaAlignment._statementGroupToJSON($(this)); if (statementGroupJSON !== null) { statementGroupLst.push(statementGroupJSON); } @@ -404,13 +442,13 @@ SchemaAlignmentDialog._itemToJSON = function (item) { var nameDescLst = new Array(); var nameDescsDom = item.find('.wbs-namedesc'); nameDescsDom.each(function () { - var nameDescJSON = SchemaAlignmentDialog._nameDescToJSON($(this)); + var nameDescJSON = SchemaAlignment._nameDescToJSON($(this)); if (nameDescJSON !== null) { nameDescLst.push(nameDescJSON); } }); var inputContainer = item.find(".wbs-item-input").first(); - var subjectJSON = SchemaAlignmentDialog._inputContainerToJSON(inputContainer); + var subjectJSON = SchemaAlignment._inputContainerToJSON(inputContainer); if (subjectJSON !== null && statementGroupLst.length === statementsDom.length && nameDescLst.length === nameDescsDom.length) { @@ -426,7 +464,7 @@ SchemaAlignmentDialog._itemToJSON = function (item) { * NAMES AND DESCRIPTIONS * **************************/ -SchemaAlignmentDialog._addNameDesc = function(item, json) { +SchemaAlignment._addNameDesc = function(item, json) { var term_type = 'ALIAS'; var value = null; var override = false; @@ -442,28 +480,28 @@ SchemaAlignmentDialog._addNameDesc = function(item, json) { var type_input = $('').appendTo(type_container); $('') .val('LABEL') - .text($.i18n('wikidata-schema/label')) + .text($.i18n('wikibase-schema/label')) .appendTo(type_input); $('') .val('DESCRIPTION') - .text($.i18n('wikidata-schema/description')) + .text($.i18n('wikibase-schema/description')) .appendTo(type_input); $('') .val('ALIAS') - .text($.i18n('wikidata-schema/alias')) + .text($.i18n('wikibase-schema/alias')) .appendTo(type_input); type_input.val(term_type); var toolbar = $('
').addClass('wbs-toolbar').appendTo(namedesc); - SchemaAlignmentDialog._makeDeleteButton().click(function(e) { + SchemaAlignment._makeDeleteButton().click(function(e) { namedesc.remove(); - SchemaAlignmentDialog._hasChanged(); + SchemaAlignment._hasChanged(); e.preventDefault(); }).appendTo(toolbar); - var right = $('
').addClass('wbs-right').appendTo(namedesc); + $('
').addClass('wbs-right').appendTo(namedesc); var value_container = $('
').addClass('wbs-namedesc-value').appendTo(namedesc); - SchemaAlignmentDialog._initField(value_container, "monolingualtext", value); + SchemaAlignment._initField(value_container, "monolingualtext", value); var override_container = $('
').addClass('wbs-namedesc-override').appendTo(namedesc); var label = $('').appendTo(override_container); @@ -471,9 +509,9 @@ SchemaAlignmentDialog._addNameDesc = function(item, json) { .attr('type', 'checkbox') .prop('checked', override) .appendTo(label); - var span = $('').text($.i18n('wikidata-schema/override-term')).appendTo(label); + $('').text($.i18n('wikibase-schema/override-term')).appendTo(label); checkbox.on('change', function(e) { - SchemaAlignmentDialog._hasChanged(); + SchemaAlignment._hasChanged(); }); type_input.on('change', function(e) { var checkbox_visible = type_input.val() !== 'ALIAS'; @@ -482,12 +520,12 @@ SchemaAlignmentDialog._addNameDesc = function(item, json) { } else { override_container.hide(); } - SchemaAlignmentDialog._hasChanged(); + SchemaAlignment._hasChanged(); }); -} +}; -SchemaAlignmentDialog._nameDescToJSON = function (namedesc) { +SchemaAlignment._nameDescToJSON = function (namedesc) { var term_type = namedesc.find('select').first().val(); var type = term_type; if (term_type !== 'ALIAS') { @@ -502,14 +540,14 @@ SchemaAlignmentDialog._nameDescToJSON = function (namedesc) { name_type: type, value: value, }; -} +}; /******************** * STATEMENT GROUPS * ********************/ -SchemaAlignmentDialog._addStatementGroup = function(item, json) { +SchemaAlignment._addStatementGroup = function(item, json) { var property = null; var statements = null; if (json) { @@ -522,15 +560,15 @@ SchemaAlignmentDialog._addStatementGroup = function(item, json) { var inputContainer = $('
').addClass('wbs-prop-input').appendTo(statementGroup); var right = $('
').addClass('wbs-right').appendTo(statementGroup); var statementContainer = $('
').addClass('wbs-statement-container').appendTo(right); - SchemaAlignmentDialog._initPropertyField(inputContainer, statementContainer, property); + SchemaAlignment._initPropertyField(inputContainer, statementContainer, property); var toolbar = $('
').addClass('wbs-toolbar').appendTo(right); var addValueButton = $('').addClass('wbs-add-statement').click(function(e) { var datatype = inputContainer.data("jsonValue").datatype; - SchemaAlignmentDialog._addStatement(statementContainer, datatype, null); + SchemaAlignment._addStatement(statementContainer, datatype, null); e.preventDefault(); }).appendTo(toolbar).hide(); - SchemaAlignmentDialog._plusButton($.i18n('wikidata-schema/add-value'), addValueButton); - var removeButton = SchemaAlignmentDialog._makeDeleteButton() + SchemaAlignment._plusButton($.i18n('wikibase-schema/add-value'), addValueButton); + var removeButton = SchemaAlignment._makeDeleteButton() .addClass('wbs-remove-statement-group') .appendTo(toolbar) .click(function(e) { @@ -542,7 +580,7 @@ SchemaAlignmentDialog._addStatementGroup = function(item, json) { if (statements) { for (var i = 0; i != statements.length; i++) { - SchemaAlignmentDialog._addStatement(statementContainer, property.datatype, statements[i]); + SchemaAlignment._addStatement(statementContainer, property.datatype, statements[i]); addValueButton.show(); removeButton.hide(); } @@ -550,19 +588,19 @@ SchemaAlignmentDialog._addStatementGroup = function(item, json) { inputContainer.find('input').focus(); } -} +}; -SchemaAlignmentDialog._statementGroupToJSON = function (statementGroup) { +SchemaAlignment._statementGroupToJSON = function (statementGroup) { var lst = new Array(); var domStatements = statementGroup.find('.wbs-statement-container').first().children('.wbs-statement'); domStatements.each(function () { - var statementJSON = SchemaAlignmentDialog._statementToJSON($(this)); + var statementJSON = SchemaAlignment._statementToJSON($(this)); if (statementJSON !== null) { lst.push(statementJSON); } }); var inputContainer = statementGroup.find(".wbs-prop-input").first(); - var propertyJSON = SchemaAlignmentDialog._inputContainerToJSON(inputContainer); + var propertyJSON = SchemaAlignment._inputContainerToJSON(inputContainer); if (propertyJSON !== null && domStatements.length === lst.length && lst.length > 0) { return {property: propertyJSON, statements: lst}; @@ -575,7 +613,7 @@ SchemaAlignmentDialog._statementGroupToJSON = function (statementGroup) { * STATEMENTS * **************/ -SchemaAlignmentDialog._addStatement = function(container, datatype, json) { +SchemaAlignment._addStatement = function(container, datatype, json) { var qualifiers = null; var references = null; var value = null; @@ -587,15 +625,15 @@ SchemaAlignmentDialog._addStatement = function(container, datatype, json) { var statement = $('
').addClass('wbs-statement'); var inputContainer = $('
').addClass('wbs-target-input').appendTo(statement); - SchemaAlignmentDialog._initField(inputContainer, datatype, value); + SchemaAlignment._initField(inputContainer, datatype, value); // If we are in a mainsnak... // (see https://www.mediawiki.org/wiki/Wikibase/DataModel#Snaks) if (container.parents('.wbs-statement').length == 0) { // add delete button var toolbar1 = $('
').addClass('wbs-toolbar').appendTo(statement); - SchemaAlignmentDialog._makeDeleteButton().click(function(e) { - SchemaAlignmentDialog._removeStatement(statement); + SchemaAlignment._makeDeleteButton().click(function(e) { + SchemaAlignment._removeStatement(statement); e.preventDefault(); }).appendTo(toolbar1); @@ -608,14 +646,14 @@ SchemaAlignmentDialog._addStatement = function(container, datatype, json) { var toolbar2 = $('
').addClass('wbs-toolbar').appendTo(right); var addQualifierButton = $('').addClass('wbs-add-qualifier') .click(function(e) { - SchemaAlignmentDialog._addQualifier(qualifierContainer, null); + SchemaAlignment._addQualifier(qualifierContainer, null); e.preventDefault(); }).appendTo(toolbar2); - SchemaAlignmentDialog._plusButton($.i18n('wikidata-schema/add-qualifier'), addQualifierButton); + SchemaAlignment._plusButton($.i18n('wikibase-schema/add-qualifier'), addQualifierButton); if (qualifiers) { for (var i = 0; i != qualifiers.length; i++) { - SchemaAlignmentDialog._addQualifier(qualifierContainer, qualifiers[i]); + SchemaAlignment._addQualifier(qualifierContainer, qualifiers[i]); } } @@ -638,30 +676,30 @@ SchemaAlignmentDialog._addStatement = function(container, datatype, json) { var addReferenceButton = $('').addClass('wbs-add-reference') .click(function(e) { referenceContainer.show(); - SchemaAlignmentDialog._addReference(referenceContainer, null); - SchemaAlignmentDialog._updateReferencesNumber(referenceContainer); + SchemaAlignment._addReference(referenceContainer, null); + SchemaAlignment._updateReferencesNumber(referenceContainer); e.preventDefault(); }).appendTo(toolbar3); - SchemaAlignmentDialog._plusButton($.i18n('wikidata-schema/add-reference'), addReferenceButton); + SchemaAlignment._plusButton($.i18n('wikibase-schema/add-reference'), addReferenceButton); var pasteToolbar = $('
').addClass('wbs-toolbar').appendTo(referencesToggleContainer); var referencePaste = $('') .addClass('wbs-paste-reference') .appendTo(pasteToolbar); - if (SchemaAlignmentDialog._copiedReference === null) { + if (SchemaAlignment._copiedReference === null) { referencePaste.hide(); } - var pasteIcon = $('').addClass('wbs-icon').appendTo(referencePaste); - var referencePasteButton = $('') + $('').addClass('wbs-icon').appendTo(referencePaste); + $('') .addClass('wbs-paste-reference-button') - .text($.i18n('wikidata-schema/paste-reference')) + .text($.i18n('wikibase-schema/paste-reference')) .appendTo(referencePaste) .click(function(e) { - if (SchemaAlignmentDialog._copiedReference !== null) { - SchemaAlignmentDialog._addReference(referenceContainer, SchemaAlignmentDialog._copiedReference); - SchemaAlignmentDialog._updateReferencesNumber(referenceContainer); + if (SchemaAlignment._copiedReference !== null) { + SchemaAlignment._addReference(referenceContainer, SchemaAlignment._copiedReference); + SchemaAlignment._updateReferencesNumber(referenceContainer); referencePaste.hide(); - SchemaAlignmentDialog._hasChanged(); + SchemaAlignment._hasChanged(); } e.preventDefault(); e.stopPropagation(); @@ -669,33 +707,33 @@ SchemaAlignmentDialog._addStatement = function(container, datatype, json) { if (references) { for (var i = 0; i != references.length; i++) { - SchemaAlignmentDialog._addReference(referenceContainer, references[i]); + SchemaAlignment._addReference(referenceContainer, references[i]); } } - SchemaAlignmentDialog._updateReferencesNumber(referenceContainer); + SchemaAlignment._updateReferencesNumber(referenceContainer); } container.append(statement); -} +}; -SchemaAlignmentDialog._statementToJSON = function (statement) { +SchemaAlignment._statementToJSON = function (statement) { var inputContainer = statement.find(".wbs-target-input").first(); var qualifiersList = new Array(); var referencesList = new Array(); var qualifiersDom = statement.find('.wbs-qualifier-container').first().children(); qualifiersDom.each(function () { - var qualifierJSON = SchemaAlignmentDialog._qualifierToJSON($(this)); + var qualifierJSON = SchemaAlignment._qualifierToJSON($(this)); if (qualifierJSON !== null) { qualifiersList.push(qualifierJSON); } }); var referencesDom = statement.find('.wbs-reference-container').first().children(); referencesDom.each(function () { - var referenceJSON = SchemaAlignmentDialog._referenceToJSON($(this)); + var referenceJSON = SchemaAlignment._referenceToJSON($(this)); if (referenceJSON !== null) { referencesList.push(referenceJSON); } }); - var valueJSON = SchemaAlignmentDialog._inputContainerToJSON(inputContainer); + var valueJSON = SchemaAlignment._inputContainerToJSON(inputContainer); if (referencesList.length === referencesDom.length && qualifiersList.length === qualifiersDom.length && valueJSON !== null) { @@ -713,7 +751,7 @@ SchemaAlignmentDialog._statementToJSON = function (statement) { * QUALIFIERS * **************/ -SchemaAlignmentDialog._addQualifier = function(container, json) { +SchemaAlignment._addQualifier = function(container, json) { var property = null; var value = null; if (json) { @@ -725,27 +763,27 @@ SchemaAlignmentDialog._addQualifier = function(container, json) { var toolbar1 = $('
').addClass('wbs-toolbar').appendTo(qualifier); var inputContainer = $('
').addClass('wbs-prop-input').appendTo(qualifier); var right = $('
').addClass('wbs-right').appendTo(qualifier); - var deleteButton = SchemaAlignmentDialog._makeDeleteButton() + var deleteButton = SchemaAlignment._makeDeleteButton() .addClass('wbs-remove-statement-group') .appendTo(toolbar1).click(function(e) { qualifier.remove(); - SchemaAlignmentDialog._hasChanged(); + SchemaAlignment._hasChanged(); e.preventDefault(); }); var statementContainer = $('
').addClass('wbs-statement-container').appendTo(right); - SchemaAlignmentDialog._initPropertyField(inputContainer, statementContainer, property); + SchemaAlignment._initPropertyField(inputContainer, statementContainer, property); if (value && property) { - SchemaAlignmentDialog._addStatement(statementContainer, property.datatype, {value:value}); + SchemaAlignment._addStatement(statementContainer, property.datatype, {value:value}); } else { inputContainer.find('input').focus(); } -} +}; -SchemaAlignmentDialog._qualifierToJSON = function(elem) { +SchemaAlignment._qualifierToJSON = function(elem) { var prop = elem.find(".wbs-prop-input").first(); var target = elem.find(".wbs-target-input").first(); - var propJSON = SchemaAlignmentDialog._inputContainerToJSON(prop); - var valueJSON = SchemaAlignmentDialog._inputContainerToJSON(target); + var propJSON = SchemaAlignment._inputContainerToJSON(prop); + var valueJSON = SchemaAlignment._inputContainerToJSON(target); if (propJSON !== null && valueJSON !== null) { return { prop: propJSON, @@ -754,13 +792,13 @@ SchemaAlignmentDialog._qualifierToJSON = function(elem) { } else { return null; } -} +}; /************** * REFERENCES * **************/ -SchemaAlignmentDialog._addReference = function(container, json) { +SchemaAlignment._addReference = function(container, json) { var snaks = null; if (json) { snaks = json.snaks; @@ -772,21 +810,21 @@ SchemaAlignmentDialog._addReference = function(container, json) { var referenceCopyIcon = $('').addClass('wbs-icon').appendTo(referenceCopy); var copyButton = $('') .addClass('wbs-copy-reference-button') - .text($.i18n('wikidata-schema/copy-reference')) + .text($.i18n('wikibase-schema/copy-reference')) .appendTo(referenceCopy) .click(function(e) { - if (SchemaAlignmentDialog._copyReference(reference)) { - $(this).text($.i18n('wikidata-schema/reference-copied')) + if (SchemaAlignment._copyReference(reference)) { + $(this).text($.i18n('wikibase-schema/reference-copied')) .parent().addClass('wbs-copied-reference'); container.parent().parent().find('.wbs-paste-reference').hide(); } e.preventDefault(); }); var toolbarRef = $('
').addClass('wbs-toolbar').appendTo(referenceHeader); - SchemaAlignmentDialog._makeDeleteButton().click(function(e) { + SchemaAlignment._makeDeleteButton().click(function(e) { reference.remove(); - SchemaAlignmentDialog._updateReferencesNumber(container); - SchemaAlignmentDialog._hasChanged(); + SchemaAlignment._updateReferencesNumber(container); + SchemaAlignment._hasChanged(); e.preventDefault(); }).appendTo(toolbarRef); var right = $('
').addClass('wbs-right').appendTo(reference); @@ -794,25 +832,25 @@ SchemaAlignmentDialog._addReference = function(container, json) { var toolbar2 = $('
').addClass('wbs-toolbar').appendTo(right); var addSnakButton = $('').addClass('wbs-add-qualifier') .click(function(e) { - SchemaAlignmentDialog._addQualifier(qualifierContainer, null); + SchemaAlignment._addQualifier(qualifierContainer, null); e.preventDefault(); }).appendTo(toolbar2); - SchemaAlignmentDialog._plusButton($.i18n('wikidata-schema/add-reference-snak'), addSnakButton); + SchemaAlignment._plusButton($.i18n('wikibase-schema/add-reference-snak'), addSnakButton); if (snaks) { for (var i = 0; i != snaks.length; i++) { - SchemaAlignmentDialog._addQualifier(qualifierContainer, snaks[i]); + SchemaAlignment._addQualifier(qualifierContainer, snaks[i]); } } else { - SchemaAlignmentDialog._addQualifier(qualifierContainer, null); + SchemaAlignment._addQualifier(qualifierContainer, null); } -} +}; -SchemaAlignmentDialog._referenceToJSON = function(reference) { +SchemaAlignment._referenceToJSON = function(reference) { var snaks = reference.find('.wbs-qualifier-container').first().children(); var snaksList = new Array(); snaks.each(function () { - var qualifier = SchemaAlignmentDialog._qualifierToJSON($(this)); + var qualifier = SchemaAlignment._qualifierToJSON($(this)); if (qualifier !== null) { snaksList.push(qualifier); } @@ -822,38 +860,38 @@ SchemaAlignmentDialog._referenceToJSON = function(reference) { } else { return null; } -} +}; -SchemaAlignmentDialog._updateReferencesNumber = function(container) { +SchemaAlignment._updateReferencesNumber = function(container) { var childrenCount = container.children().length; var statement = container.parents('.wbs-statement'); var a = statement.find('.wbs-references-toggle a').first(); - a.html(childrenCount+$.i18n('wikidata-schema/nb-references')); -} + a.html(childrenCount+$.i18n('wikibase-schema/nb-references')); +}; -SchemaAlignmentDialog._copyReference = function(reference) { +SchemaAlignment._copyReference = function(reference) { // mark any other copied reference as not copied $('.wbs-copy-reference-button') - .text($.i18n('wikidata-schema/copy-reference')); + .text($.i18n('wikibase-schema/copy-reference')); $('.wbs-copy-reference') .removeClass('wbs-copied-reference'); - var copiedReference = SchemaAlignmentDialog._referenceToJSON(reference); + var copiedReference = SchemaAlignment._referenceToJSON(reference); if (copiedReference !== null) { - SchemaAlignmentDialog._copiedReference = copiedReference; + SchemaAlignment._copiedReference = copiedReference; $('.wbs-paste-reference').show(); return true; } else { return false; } -} +}; /************************ * FIELD INITIALIZATION * ************************/ -SchemaAlignmentDialog._getPropertyType = function(pid, callback) { +SchemaAlignment._getPropertyType = function(pid, callback) { $.ajax({ - url:'https://www.wikidata.org/w/api.php', + url: WikibaseManager.getSelectedWikibaseApi(), data: { action: "wbgetentities", format: "json", @@ -864,35 +902,34 @@ SchemaAlignmentDialog._getPropertyType = function(pid, callback) { success: function(data) { callback(data.entities[pid].datatype); }}); -} +}; -SchemaAlignmentDialog._initPropertyField = function(inputContainer, targetContainer, initialValue) { +SchemaAlignment._initPropertyField = function(inputContainer, targetContainer, initialValue) { var input = $('').appendTo(inputContainer); - input.attr("placeholder", $.i18n('wikidata-schema/property-placeholder')); + input.attr("placeholder", $.i18n('wikibase-schema/property-placeholder')); if (this._reconService !== null) { - endpoint = this._reconService.suggest.property; + var endpoint = this._reconService.suggest.property; var suggestConfig = $.extend({}, endpoint); suggestConfig.key = null; suggestConfig.query_param_name = "prefix"; input.suggestP(suggestConfig).bind("fb-select", function(evt, data) { // Fetch the type of this property and add the appropriate target value type - var statementGroup = inputContainer.parents(".wbs-statement-group, .wbs-qualifier").first(); - SchemaAlignmentDialog._getPropertyType(data.id, function(datatype) { + SchemaAlignment._getPropertyType(data.id, function(datatype) { inputContainer.data("jsonValue", { type : "wbpropconstant", pid : data.id, label: data.name, datatype: datatype, }); - SchemaAlignmentDialog._addStatement(targetContainer, datatype, null); + SchemaAlignment._addStatement(targetContainer, datatype, null); var addValueButtons = targetContainer.parent().find('.wbs-add-statement'); var removeGroupButton = targetContainer.parent().find('.wbs-remove-statement-group'); removeGroupButton.hide(); addValueButtons.show(); }); - SchemaAlignmentDialog._hasChanged(); + SchemaAlignment._hasChanged(); }).bind("fb-textchange", function(evt, data) { inputContainer.data("jsonValue", null); targetContainer.find('.wbs-statement').remove(); @@ -914,20 +951,20 @@ SchemaAlignmentDialog._initPropertyField = function(inputContainer, targetContai inputContainer.data("jsonValue", initialValue); } -} +}; -SchemaAlignmentDialog._initField = function(inputContainer, mode, initialValue, changedCallback) { +SchemaAlignment._initField = function(inputContainer, mode, initialValue, changedCallback) { var input = $('').appendTo(inputContainer); if (! changedCallback) { - changedCallback = SchemaAlignmentDialog._hasChanged; + changedCallback = SchemaAlignment._hasChanged; } if (this._reconService !== null && (mode === "wikibase-item" || mode === "unit")) { if (mode === "wikibase-item") { - input.attr("placeholder", $.i18n('wikidata-schema/item-or-reconciled-column')); + input.attr("placeholder", $.i18n('wikibase-schema/item-or-reconciled-column')); } else { - input.attr("placeholder", $.i18n('wikidata-schema/unit')); + input.attr("placeholder", $.i18n('wikibase-schema/unit')); } var endpoint = null; endpoint = this._reconService.suggest.entity; @@ -984,7 +1021,7 @@ SchemaAlignmentDialog._initField = function(inputContainer, mode, initialValue, changedCallback(); }); - SchemaAlignmentDialog.setupStringInputValidation(input, /^((\d{4}(-[0-1]\d(-[0-3]\d)?)?)|TODAY)$/); + SchemaAlignment.setupStringInputValidation(input, /^((\d{4}(-[0-1]\d(-[0-3]\d)?)?)|TODAY)$/); } else if (mode === "globe-coordinate") { input.attr("placeholder", "lat,lon"); var propagateValue = function(val) { @@ -1000,7 +1037,7 @@ SchemaAlignmentDialog._initField = function(inputContainer, mode, initialValue, changedCallback(); }); - SchemaAlignmentDialog.setupStringInputValidation(input, /^[\-+]?\d+(\.\d*)?[,\/][\-+]?\d+(\.\d*)?([,\/]\d+(\.\d*)?)?$/); + SchemaAlignment.setupStringInputValidation(input, /^[\-+]?\d+(\.\d*)?[,\/][\-+]?\d+(\.\d*)?([,\/]\d+(\.\d*)?)?$/); } else if (mode === "language") { input.attr("placeholder", "lang"); input.addClass("wbs-language-input"); @@ -1039,10 +1076,10 @@ SchemaAlignmentDialog._initField = function(inputContainer, mode, initialValue, value: inputContainerValue.data("jsonValue"), }); changedCallback(); - } + }; - SchemaAlignmentDialog._initField(inputContainerLanguage, "language", langValue, propagateValue); - SchemaAlignmentDialog._initField(inputContainerValue, "string", strValue, propagateValue); + SchemaAlignment._initField(inputContainerLanguage, "language", langValue, propagateValue); + SchemaAlignment._initField(inputContainerValue, "string", strValue, propagateValue); } else if (mode === "quantity") { input.remove(); @@ -1069,10 +1106,10 @@ SchemaAlignmentDialog._initField = function(inputContainer, mode, initialValue, unit: inputContainerUnit.data("jsonValue"), }); changedCallback(); - } + }; - SchemaAlignmentDialog._initField(inputContainerAmount, "amount", amountValue, propagateValue); - SchemaAlignmentDialog._initField(inputContainerUnit, "unit", unitValue, propagateValue); + SchemaAlignment._initField(inputContainerAmount, "amount", amountValue, propagateValue); + SchemaAlignment._initField(inputContainerUnit, "unit", unitValue, propagateValue); } else { var propagateValue = function(val) { @@ -1087,23 +1124,23 @@ SchemaAlignmentDialog._initField = function(inputContainer, mode, initialValue, changedCallback(); }); if (mode === "amount") { - input.attr("placeholder", $.i18n('wikidata-schema/amount')); - SchemaAlignmentDialog.setupStringInputValidation(input, /^[\-+]?\d+(\.\d*)?(E[\-+]\d+)?$/); + input.attr("placeholder", $.i18n('wikibase-schema/amount')); + SchemaAlignment.setupStringInputValidation(input, /^[\-+]?\d+(\.\d*)?(E[\-+]\d+)?$/); } else if (mode === "url") { - input.attr("placeholder", $.i18n('wikidata-schema/full-url')); - SchemaAlignmentDialog.setupStringInputValidation(input, /^https?:\/\/.+$/); + input.attr("placeholder", $.i18n('wikibase-schema/full-url')); + SchemaAlignment.setupStringInputValidation(input, /^https?:\/\/.+$/); } else if (mode === "tabular-data") { - input.attr("placeholder", $.i18n('wikidata-schema/tabular-data-with-prefix')); - SchemaAlignmentDialog.setupStringInputValidation(input, /^Data:.+$/); + input.attr("placeholder", $.i18n('wikibase-schema/tabular-data-with-prefix')); + SchemaAlignment.setupStringInputValidation(input, /^Data:.+$/); } else if (mode === "commonsMedia") { - input.attr("placeholder", $.i18n('wikidata-schema/commons-media')); + input.attr("placeholder", $.i18n('wikibase-schema/commons-media')); } else if (mode === "math") { - input.attr("placeholder", $.i18n('wikidata-schema/math-expression')); + input.attr("placeholder", $.i18n('wikibase-schema/math-expression')); } else if (mode === "geo-shape") { - input.attr("placeholder", $.i18n('wikidata-schema/geoshape-with-prefix')); - SchemaAlignmentDialog.setupStringInputValidation(input, /^Data:.+$/); + input.attr("placeholder", $.i18n('wikibase-schema/geoshape-with-prefix')); + SchemaAlignment.setupStringInputValidation(input, /^Data:.+$/); } else { - SchemaAlignmentDialog.setupStringInputValidation(input, /^.+$/); + SchemaAlignment.setupStringInputValidation(input, /^.+$/); } if (mode !== "external-id" && mode !== "url" && @@ -1113,7 +1150,7 @@ SchemaAlignmentDialog._initField = function(inputContainer, mode, initialValue, mode !== "commonsMedia" && mode !== "geo-shape" && mode !== "math") { - alert($.i18n('wikidata-schema/datatype-not-supported-yet')); + alert($.i18n('wikibase-schema/datatype-not-supported-yet')); } } @@ -1125,8 +1162,8 @@ SchemaAlignmentDialog._initField = function(inputContainer, mode, initialValue, var origText = column.text(); column.text(""); column.append($('
').addClass('wbs-restricted-column-name').text(origText)); - var deleteButton = SchemaAlignmentDialog._makeDeleteButton(true).appendTo(column); - deleteButton.attr('alt', $.i18n('wikidata-schema/remove-column')); + var deleteButton = SchemaAlignment._makeDeleteButton(true).appendTo(column); + deleteButton.attr('alt', $.i18n('wikibase-schema/remove-column')); deleteButton.click(function (e) { columnDiv.remove(); input.show(); @@ -1177,7 +1214,7 @@ SchemaAlignmentDialog._initField = function(inputContainer, mode, initialValue, input.val(initialValue.label); input.addClass("wbs-validated-input"); } else if (initialValue.type == "wbitemvariable") { - var cell = SchemaAlignmentDialog._createDraggableColumn(initialValue.columnName, true); + var cell = SchemaAlignment._createDraggableColumn(initialValue.columnName, true); acceptDraggableColumn(cell); } else if (initialValue.type === "wbstringconstant" || initialValue.type === "wbdateconstant" || @@ -1190,14 +1227,14 @@ SchemaAlignmentDialog._initField = function(inputContainer, mode, initialValue, initialValue.type === "wbdatevariable" || initialValue.type === "wblocationvariable" || initialValue.type === "wblanguagevariable") { - var cell = SchemaAlignmentDialog._createDraggableColumn(initialValue.columnName, false); + var cell = SchemaAlignment._createDraggableColumn(initialValue.columnName, false); acceptDraggableColumn(cell); } inputContainer.data("jsonValue", initialValue); } -} +}; -SchemaAlignmentDialog.setupStringInputValidation = function(input, regex) { +SchemaAlignment.setupStringInputValidation = function(input, regex) { input.focus(function() { input.removeClass('wbs-unvalidated-input'); }).blur(function() { @@ -1208,9 +1245,9 @@ SchemaAlignmentDialog.setupStringInputValidation = function(input, regex) { input.addClass('wbs-unvalidated-input'); } }); -} +}; -SchemaAlignmentDialog._inputContainerToJSON = function (inputContainer) { +SchemaAlignment._inputContainerToJSON = function (inputContainer) { var data = inputContainer.data(); if (data && 'jsonValue' in data) { return data.jsonValue; @@ -1219,57 +1256,62 @@ SchemaAlignmentDialog._inputContainerToJSON = function (inputContainer) { } }; -SchemaAlignmentDialog._removeStatement = function(statement) { +SchemaAlignment._removeStatement = function(statement) { var statementGroup = statement.parents('.wbs-statement-group, .wbs-qualifier').first(); statement.remove(); var remainingStatements = statementGroup.find('.wbs-statement').length; if (remainingStatements === 0) { statementGroup.remove(); } - SchemaAlignmentDialog._hasChanged(); -} + SchemaAlignment._hasChanged(); +}; -SchemaAlignmentDialog.getJSON = function() { - var list = new Array(); +SchemaAlignment.getJSON = function() { + var list = []; var itemsDom = $('#schema-alignment-statements-container .wbs-item'); itemsDom.each(function () { - var itemJSON = SchemaAlignmentDialog._itemToJSON($(this)); + var itemJSON = SchemaAlignment._itemToJSON($(this)); if (itemJSON !== null) { list.push(itemJSON); } }); if (list.length === itemsDom.length) { return { - 'itemDocuments': list, - 'wikibasePrefix': this._wikibasePrefix, + itemDocuments: list, + siteIri: WikibaseManager.getSelectedWikibaseSiteIri(), + mediaWikiApiEndpoint: WikibaseManager.getSelectedWikibaseApi(), + editGroupsURLSchema: WikibaseManager.getSelectedWikibaseEditGroupsURLSchema() }; } else { return null; } }; -SchemaAlignmentDialog._hasChanged = function() { - SchemaAlignmentDialog._hasUnsavedChanges = true; - SchemaAlignmentDialog.preview(); - SchemaAlignmentDialog._unsavedIndicator.show(); - SchemaAlignmentDialog._schemaElmts.saveButton +SchemaAlignment._hasChanged = function() { + SchemaAlignment._hasUnsavedChanges = true; + SchemaAlignment.preview(); + SchemaAlignment._unsavedIndicator.show(); + SchemaAlignment._schemaElmts.saveButton .prop('disabled', false) .removeClass('disabled'); - SchemaAlignmentDialog._schemaElmts.discardButton + SchemaAlignment._schemaElmts.discardButton .prop('disabled', false) .removeClass('disabled'); $('.wbs-copy-reference-button') - .text($.i18n('wikidata-schema/copy-reference')); + .text($.i18n('wikibase-schema/copy-reference')); $('.wbs-copy-reference') .removeClass('wbs-copied-reference'); -} +}; -SchemaAlignmentDialog.updateNbEdits = function(nb_edits) { - this._previewElmts.previewExplanation.text( - $.i18n('wikidata-schema/preview-explanation').replace('{nb_edits}',nb_edits)); -} +SchemaAlignment.updateNbEdits = function(nb_edits) { + this._previewElmts.previewExplanation.html($.i18n('wikibase-schema/preview-explanation', + nb_edits, + WikibaseManager.getSelectedWikibaseMainPage(), + WikibaseManager.getSelectedWikibaseName() + )); +}; -SchemaAlignmentDialog.preview = function() { +SchemaAlignment.preview = function() { var self = this; $('.invalid-schema-warning').hide(); @@ -1284,7 +1326,7 @@ SchemaAlignmentDialog.preview = function() { } Refine.postCSRF( "command/wikidata/preview-wikibase-schema?" + $.param({ project: theProject.id }), - { schema: JSON.stringify(schema), engine: JSON.stringify(ui.browsingEngine.getJSON()) }, + { schema: JSON.stringify(schema), manifest: JSON.stringify(WikibaseManager.getSelectedWikibase()), engine: JSON.stringify(ui.browsingEngine.getJSON()) }, function(data) { self.issueSpinner.hide(); self.previewSpinner.hide(); @@ -1308,27 +1350,27 @@ SchemaAlignmentDialog.preview = function() { ); }; -Refine.registerUpdateFunction(function(options) { - // Inject tabs in any project where the schema has been defined - if(theProject.overlayModels.wikibaseSchema && !SchemaAlignmentDialog.isSetUp()) { - SchemaAlignmentDialog.setUpTabs(); - } - if (SchemaAlignmentDialog.isSetUp() && (options.everythingChanged || options.modelsChanged || - options.rowsChanged || options.rowMetadataChanged || options.cellsChanged || options.engineChanged)) { - if (!SchemaAlignmentDialog._hasUnsavedChanges) { - SchemaAlignmentDialog._discardChanges(); - } - SchemaAlignmentDialog.updateColumns(); - SchemaAlignmentDialog.preview(); - } -}); +// Used for injecting tabs in any project where the schema has been defined. +SchemaAlignment.onProjectUpdate = function(options) { + if(theProject.overlayModels.wikibaseSchema && !SchemaAlignment.isSetUp()) { + SchemaAlignment.setUpTabs(); + } + if (SchemaAlignment.isSetUp() && (options.everythingChanged || options.modelsChanged || + options.rowsChanged || options.rowMetadataChanged || options.cellsChanged || options.engineChanged)) { + if (!SchemaAlignment._hasUnsavedChanges) { + SchemaAlignment._discardChanges(); + } + SchemaAlignment.updateColumns(); + SchemaAlignment.preview(); + } +}; /************************* * WARNINGS RENDERING * *************************/ -SchemaAlignmentDialog._updateWarnings = function(warnings, totalCount) { - var mainDiv = $('#wikidata-issues-panel'); +SchemaAlignment._updateWarnings = function(warnings, totalCount) { + var mainDiv = $('#wikibase-issues-panel'); var countsElem = this.issuesTabCount; // clear everything @@ -1346,5 +1388,5 @@ SchemaAlignmentDialog._updateWarnings = function(warnings, totalCount) { countsElem.text(totalCount); countsElem.show(); } -} +}; diff --git a/extensions/wikidata/module/scripts/warningsrenderer.js b/extensions/wikidata/module/scripts/warningsrenderer.js index ae27aadd6..f4be7c92d 100644 --- a/extensions/wikidata/module/scripts/warningsrenderer.js +++ b/extensions/wikidata/module/scripts/warningsrenderer.js @@ -1,74 +1,75 @@ var WarningsRenderer = {}; // renders a Wikibase entity into a link -WarningsRenderer._renderEntity = function(entity) { +WarningsRenderer._renderEntity = function (entity) { if (!entity.id && entity.value) { - entity.id = entity.value.id; + entity.id = entity.value.id; } var id = entity.id; - var is_new = entity.siteIri == "http://localhost/entity/"; + var is_new = entity.siteIri === "http://localhost/entity/"; if (is_new) { - id = $.i18n('wikidata-preview/new-id'); + id = $.i18n('wikibase-preview/new-id'); } var fullLabel = id; if (entity.label) { - fullLabel = entity.label + ' (' + id + ')'; + fullLabel = entity.label + ' (' + id + ')'; } var url = entity.iri; if (!url && entity.value) { - url = 'http://www.wikidata.org/entity/'+entity.value.id; + url = WikibaseManager.getSelectedWikibaseSiteIri() + entity.value.id; } if (is_new) { - return ''+fullLabel+''; + return '' + fullLabel + ''; } else { - return ''+fullLabel+''; + return '' + fullLabel + ''; } -} +}; // replaces the issue properties in localization template -WarningsRenderer._replaceIssueProperties = function(template, properties) { +WarningsRenderer._replaceIssueProperties = function (template, properties) { + template = template.replace(new RegExp('{wikibase_name}', 'g'), WikibaseManager.getSelectedWikibaseName); if (!properties) { return template; } var expanded = template; for (var key in properties) { if (properties.hasOwnProperty(key)) { - var rendered = properties[key]; - if (key.endsWith('_entity')) { - rendered = WarningsRenderer._renderEntity(properties[key]); - } - expanded = expanded.replace(new RegExp('{'+key+'}', 'g'), rendered); + var rendered = properties[key]; + if (key.endsWith('_entity')) { + rendered = WarningsRenderer._renderEntity(properties[key]); + } + expanded = expanded.replace(new RegExp('{' + key + '}', 'g'), rendered); } } return expanded; -} +}; -WarningsRenderer._renderWarning = function(warning) { - var title = WarningsRenderer._replaceIssueProperties($.i18n('warnings-messages/'+warning.type+'/title'), warning.properties); - var body = WarningsRenderer._replaceIssueProperties($.i18n('warnings-messages/'+warning.type+'/body'), warning.properties); +WarningsRenderer._renderWarning = function (warning) { + var title = WarningsRenderer._replaceIssueProperties($.i18n('warnings-messages/' + warning.type + '/title'), warning.properties); + var body = WarningsRenderer._replaceIssueProperties($.i18n('warnings-messages/' + warning.type + '/body'), warning.properties); var tr = $('').addClass('wb-warning'); var severityTd = $('') - .addClass('wb-warning-severity') - .addClass('wb-warning-severity-'+warning.severity) - .appendTo(tr); + .addClass('wb-warning-severity') + .addClass('wb-warning-severity-' + warning.severity) + .appendTo(tr); var bodyTd = $('') - .addClass('wb-warning-body') - .appendTo(tr); + .addClass('wb-warning-body') + .appendTo(tr); var h1 = $('

') - .html(title) - .appendTo(bodyTd); + .html(title) + .appendTo(bodyTd); var p = $('

') - .html(body) - .appendTo(bodyTd); + .html(body) + .appendTo(bodyTd); var countTd = $('') - .addClass('wb-warning-count') - .appendTo(tr); + .addClass('wb-warning-count') + .appendTo(tr); var countSpan = $('') - .text(warning.count) - .appendTo(countTd); + .text(warning.count) + .appendTo(countTd); return tr; -} +}; diff --git a/extensions/wikidata/module/scripts/wikibase-manager.js b/extensions/wikidata/module/scripts/wikibase-manager.js new file mode 100644 index 000000000..3a7089c2e --- /dev/null +++ b/extensions/wikidata/module/scripts/wikibase-manager.js @@ -0,0 +1,190 @@ +/** + * Manages Wikibase instances. + */ +const WikibaseManager = { + selected: "Wikidata", + wikibases: { + "Wikidata": WikidataManifestV1_0 // default one + } +}; + +WikibaseManager.getSelectedWikibase = function () { + return WikibaseManager.wikibases[WikibaseManager.selected]; +}; + +WikibaseManager.getSelectedWikibaseRoot = function () { + return WikibaseManager.getSelectedWikibase().mediawiki.root; +}; + +WikibaseManager.getSelectedWikibaseMainPage = function () { + return WikibaseManager.getSelectedWikibase().mediawiki.main_page; +}; + +WikibaseManager.getSelectedWikibaseApi = function () { + return WikibaseManager.getSelectedWikibase().mediawiki.api; +}; + +WikibaseManager.getSelectedWikibaseName = function () { + return WikibaseManager.selected; +}; + +WikibaseManager.getSelectedWikibaseSiteIri = function () { + return WikibaseManager.getSelectedWikibase().wikibase.site_iri; +}; + +WikibaseManager.getSelectedWikibaseMaxlag = function() { + return WikibaseManager.getSelectedWikibase().wikibase.maxlag; +}; + +WikibaseManager.getSelectedWikibaseOAuth = function() { + return WikibaseManager.getSelectedWikibase().oauth; +}; + +WikibaseManager.getSelectedWikibaseEditGroupsURLSchema = function() { + let editgroups = WikibaseManager.getSelectedWikibase().editgroups; + return editgroups ? editgroups.url_schema : null; +}; + +/** + * Returns the default reconciliation service URL of the Wikibase, + * such as "https://wdreconcile.toolforge.org/${lang}/api". + * + * Notice that there is a "${lang}" variable in the URL, which should + * be replaced with the actual language code. + */ +WikibaseManager.getSelectedWikibaseReconEndpoint = function () { + return WikibaseManager.getSelectedWikibase().reconciliation.endpoint; +}; + +WikibaseManager.selectWikibase = function (wikibaseName) { + if (WikibaseManager.wikibases.hasOwnProperty(wikibaseName)) { + WikibaseManager.selected = wikibaseName; + } +}; + +WikibaseManager.getAllWikibases = function () { + return WikibaseManager.wikibases; +}; + +WikibaseManager.addWikibase = function (manifest) { + WikibaseManager.wikibases[manifest.mediawiki.name] = manifest; + WikibaseManager.saveWikibases(); +}; + +WikibaseManager.removeWikibase = function (wikibaseName) { + delete WikibaseManager.wikibases[wikibaseName]; + WikibaseManager.saveWikibases(); +}; + +WikibaseManager.saveWikibases = function () { + let manifests = []; + for (let wikibaseName in WikibaseManager.wikibases) { + if (WikibaseManager.wikibases.hasOwnProperty(wikibaseName)) { + manifests.push(WikibaseManager.wikibases[wikibaseName]) + } + } + + Refine.wrapCSRF(function (token) { + $.ajax({ + async: false, + type: "POST", + url: "command/core/set-preference?" + $.param({ + name: "wikibase.manifests" + }), + data: { + "value": JSON.stringify(manifests), + csrf_token: token + }, + dataType: "json" + }); + }); +}; + +WikibaseManager.loadWikibases = function (onDone) { + $.ajax({ + url: "command/core/get-preference?" + $.param({ + name: "wikibase.manifests" + }), + success: function (data) { + if (data.value && data.value !== "null" && data.value !== "[]") { + let manifests = JSON.parse(data.value); + manifests.forEach(function (manifest) { + if (manifest.custom && manifest.custom.url && manifest.custom.last_updated + && ((Date.now() - new Date(manifest.custom.last_updated)) > 7 * 24 * 60 * 60 * 1000)) { + // If the manifest was fetched via URL and hasn't been updated for a week, + // fetch it again to keep track of the lasted version + WikibaseManager.fetchManifestFromURL(manifest.custom.url, function (newManifest) { + WikibaseManager.wikibases[newManifest.mediawiki.name] = newManifest; + WikibaseManager.saveWikibases(); + }, function () { + // fall back to the current one if failed to fetch the latest one + WikibaseManager.wikibases[manifest.mediawiki.name] = manifest; + }, true) + } else { + WikibaseManager.wikibases[manifest.mediawiki.name] = manifest; + } + }); + + WikibaseManager.selected = WikibaseManager.selectDefaultWikibaseAccordingToSavedSchema(); + + if (onDone) { + onDone(); + } + } + }, + dataType: "json" + }); +}; + +WikibaseManager.selectDefaultWikibaseAccordingToSavedSchema = function () { + let schema = theProject.overlayModels.wikibaseSchema || {}; + if (!schema.siteIri) { + return "Wikidata"; + } + + for (let wikibaseName in WikibaseManager.wikibases) { + if (WikibaseManager.wikibases.hasOwnProperty(wikibaseName)) { + let wikibase = WikibaseManager.wikibases[wikibaseName]; + if (schema.siteIri === wikibase.wikibase.site_iri) { + return wikibase.mediawiki.name; + } + } + } + + return "Wikidata"; +}; + +WikibaseManager.fetchManifestFromURL = function (manifestURL, onSuccess, onError, silent) { + let dismissBusy = function() {}; + if (!silent) { + dismissBusy = DialogSystem.showBusy($.i18n("wikibase-management/contact-service") + "..."); + } + + let _onSuccess = function (data) { + // record custom information in the manifest + data.custom = {}; + data.custom.url = manifestURL; + data.custom.last_updated = Date.now(); + if (onSuccess) { + onSuccess(data); + } + }; + + // The manifest host must support CORS. + $.ajax(manifestURL, { + "dataType": "json", + "timeout": 5000 + }).success(function (data) { + dismissBusy(); + _onSuccess(data); + }).error(function (jqXHR, textStatus, errorThrown) { + dismissBusy(); + if (!silent) { + alert($.i18n("wikibase-management/error-contact")+": " + textStatus + " : " + errorThrown + " - " + manifestURL); + } + if (onError) { + onError(); + } + }); +}; + diff --git a/extensions/wikidata/module/scripts/wikibase-manifest-schema-v1.js b/extensions/wikidata/module/scripts/wikibase-manifest-schema-v1.js new file mode 100644 index 000000000..4dfc7a7f7 --- /dev/null +++ b/extensions/wikidata/module/scripts/wikibase-manifest-schema-v1.js @@ -0,0 +1,126 @@ +const WikibaseManifestSchemaV1 = { + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "TBD", + "type": "object", + "description": "The schema validates Wikibase manifests with version 1.x. The manifest contains configurations of basic information (e.g. URL of the main page), extensions (e.g. OAuth extension) or external services (e.g. Reconciliation service) of a Wikibase", + "properties": { + "version": { + "type": "string", + "pattern": "^1\\.[0-9]+$", + "description": "The version of the Wikibase manifest, in the format of 1.x" + }, + "mediawiki": { + "type": "object", + "description": "The configurations of the MediaWiki engine", + "properties": { + "name": { + "type": "string", + "description": "The name of the Wikibase, such as 'Wikidata'" + }, + "root": { + "type": "string", + "format": "url", + "pattern": "^.*/$", + "description": "The URL of the root of the Wikibase, such as 'https://www.wikidata.org/wiki/'. The trailing slash cannot be omitted" + }, + "main_page": { + "type": "string", + "format": "url", + "description": "The URL of the main page of the Wikibase, such as 'https://www.wikidata.org/wiki/Wikidata:Main_Page'" + }, + "api": { + "type": "string", + "format": "url", + "description": "The MediaWiki API endpoint of the Wikibase, such as 'https://www.wikidata.org/w/api.php'" + } + }, + "required": ["name", "root", "main_page", "api"] + }, + "wikibase": { + "type": "object", + "description": "The configurations of the Wikibase extension", + "properties": { + "site_iri": { + "type": "string", + "format": "url", + "pattern": "^.*/$", + "description": "The IRI of the Wikibase, such as 'http://www.wikidata.org/entity/'. This should match the IRI prefixes used in RDF serialization. Be careful about using 'http' or 'https', because any variation will break comparisons at various places. The trailing slash cannot be omitted" + }, + "maxlag": { + "type": "integer", + "description": "The default maxlag of this Wikibase. For Wikidata, the default value is 5 (seconds)" + }, + "properties": { + "type": "object", + "properties": { + "instance_of": { + "type": "string", + "description": "The 'instance of' qid of the Wikibase ('P31' for Wikidata)" + }, + "subclass_of": { + "type": "string", + "description": "The 'subclass of' qid of the Wikibase ('P279' for Wikidata)" + } + }, + "required": ["instance_of", "subclass_of"] + }, + "constraints": { + "type": "object", + "description": "Constraints related qids and pids, not required since the constraints extension may not be installed", + "properties": { + "property_constraint_pid": { + "type": "string", + "description": "The property constraint pid of the Wikibase ('P2302' for Wikidata)" + } + }, + "patternProperties": { + "^.*$": { + "type": "string", + "description": "If a pid/qid is missing, constraint checks depending on it will be skipped" + } + }, + "required": ["property_constraint_pid"] + } + }, + "required": ["site_iri", "maxlag", "properties"] + }, + "oauth": { + "type": "object", + "description": "The configurations of the OAuth extension. Not required. Configuring this if and only if the OAuth extension is installed", + "properties": { + "registration_page": { + "type": "string", + "format": "url", + "description": "The url of the OAuth consumer registration page, 'https://meta.wikimedia.org/wiki/Special:OAuthConsumerRegistration/propose' for Wikidata" + } + }, + "required": ["registration_page"] + }, + "reconciliation": { + "type": "object", + "description": "The configurations of the default reconciliation service of the Wikibase", + "properties": { + "endpoint": { + "type": "string", + "format": "url", + "pattern": "^.*\\${lang}.*$", + "description": "The default reconciliation API endpoint of the Wikibase, the endpoint should include the language variable '${lang}', such as 'https://wdreconcile.toolforge.org/${lang}/api'" + } + }, + "required": ["endpoint"] + }, + "editgroups": { + "type": "object", + "description": "The configurations of the EditGroups service of the Wikibase", + "properties": { + "url_schema": { + "type": "string", + "pattern": "^.*\\${batch_id}.*$", + "description": "The URL schema used in edits summary. This is used for EditGroups to extract the batch id from a batch of edits and for linking to the EditGroups page of the batch. The URL schema must contains the variable '${batch_id}', such as '([[:toollabs:editgroups/b/OR/${batch_id}|details]])' for Wikidata" + }, + }, + "required": ["url_schema"] + } + }, + "required": ["version", "mediawiki", "wikibase", "reconciliation"] +}; diff --git a/extensions/wikidata/module/scripts/wikidata-extension-manager.js b/extensions/wikidata/module/scripts/wikidata-extension-manager.js new file mode 100644 index 000000000..294a3d91c --- /dev/null +++ b/extensions/wikidata/module/scripts/wikidata-extension-manager.js @@ -0,0 +1,6 @@ +// Make sure the wikibase manifests are loaded before initializing the schema UI. +Refine.registerUpdateFunction(function(options) { + WikibaseManager.loadWikibases(function () { + SchemaAlignment.onProjectUpdate(options); + }); +}); diff --git a/extensions/wikidata/module/scripts/wikidata-manifest-v1.0.js b/extensions/wikidata/module/scripts/wikidata-manifest-v1.0.js new file mode 100644 index 000000000..2a7e8a848 --- /dev/null +++ b/extensions/wikidata/module/scripts/wikidata-manifest-v1.0.js @@ -0,0 +1,92 @@ +const WikidataManifestV1_0 = { + "version": "1.0", + "mediawiki": { + "name": "Wikidata", + "root": "https://www.wikidata.org/wiki/", + "main_page": "https://www.wikidata.org/wiki/Wikidata:Main_Page", + "api": "https://www.wikidata.org/w/api.php" + }, + "wikibase": { + "site_iri": "http://www.wikidata.org/entity/", + "maxlag": 5, + "properties": { + "instance_of": "P31", + "subclass_of": "P279" + }, + "constraints": { + "property_constraint_pid": "P2302", + "exception_to_constraint_pid": "P2303", + "constraint_status_pid": "P2316", + "mandatory_constraint_qid": "Q21502408", + "suggestion_constraint_qid": "Q62026391", + "distinct_values_constraint_qid": "Q21502410", + "multi_value_constraint_qid": "Q21510857", + "used_as_qualifier_constraint_qid": "Q21510863", + "single_value_constraint_qid": "Q19474404", + "symmetric_constraint_qid": "Q21510862", + "type_constraint_qid": "Q21503250", + "value_type_constraint_qid": "Q21510865", + "inverse_constraint_qid": "Q21510855", + "item_requires_statement_constraint_qid": "Q21503247", + "value_requires_statement_constraint_qid": "Q21510864", + "conflicts_with_constraint_qid": "Q21502838", + "one_of_constraint_qid": "Q21510859", + "mandatory_qualifier_constraint_qid": "Q21510856", + "allowed_qualifiers_constraint_qid": "Q21510851", + "range_constraint_qid": "Q21510860", + "difference_within_range_constraint_qid": "Q21510854", + "common_link_constraint_qid": "Q21510852", + "contemporary_constraint_qid": "Q25796498", + "format_constraint_qid": "Q21502404", + "used_for_values_only_constraint_qid": "Q21528958", + "used_as_reference_constraint_qid": "Q21528959", + "no_bounds_constraint_qid": "Q51723761", + "allowed_units_constraint_qid": "Q21514353", + "single_best_value_constraint_qid": "Q52060874", + "allowed_entity_types_constraint_qid": "Q52004125", + "citation_needed_constraint_qid": "Q54554025", + "property_scope_constraint_qid": "Q53869507", + "class_pid": "P2308", + "relation_pid": "P2309", + "instance_of_relation_qid": "Q21503252", + "subclass_of_relation_qid": "Q21514624", + "instance_or_subclass_of_relation_qid": "Q30208840", + "property_pid": "P2306", + "item_of_property_constraint_pid": "P2305", + "minimum_value_pid": "P2313", + "maximum_value_pid": "P2312", + "minimum_date_pid": "P2310", + "maximum_date_pid": "P2311", + "namespace_pid": "P2307", + "format_as_a_regular_expression_pid": "P1793", + "syntax_clarification_pid": "P2916", + "constraint_scope_pid": "P4680", + "separator_pid": "P4155", + "constraint_checked_on_main_value_qid": "Q46466787", + "constraint_checked_on_qualifiers_qid": "Q46466783", + "constraint_checked_on_references_qid": "Q46466805", + "none_of_constraint_qid": "Q52558054", + "one_of_qualifier_value_property_constraint_qid": "Q52712340", + "integer_constraint_qid": "Q52848401", + "wikibase_item_qid": "Q29934200", + "wikibase_property_qid": "Q29934218", + "wikibase_lexeme_qid": "Q51885771", + "wikibase_form_qid": "Q54285143", + "wikibase_sense_qid": "Q54285715", + "wikibase_media_info_qid": "Q59712033", + "property_scope_pid": "P5314", + "as_main_value_qid": "Q54828448", + "as_qualifiers_qid": "Q54828449", + "as_references_qid": "Q54828450" + } + }, + "oauth": { + "registration_page": "https://meta.wikimedia.org/wiki/Special:OAuthConsumerRegistration/propose" + }, + "reconciliation": { + "endpoint": "https://wdreconcile.toolforge.org/${lang}/api" + }, + "editgroups": { + "url_schema": "([[:toollabs:editgroups/b/OR/${batch_id}|details]])" + } +}; diff --git a/extensions/wikidata/module/styles/dialogs/add-wikibase-dialog.less b/extensions/wikidata/module/styles/dialogs/add-wikibase-dialog.less new file mode 100644 index 000000000..df6ccfa30 --- /dev/null +++ b/extensions/wikidata/module/styles/dialogs/add-wikibase-dialog.less @@ -0,0 +1,19 @@ +@import-less url("../theme.less"); + +.add-wikibase-dialog p { + padding-top: 6px; + padding-bottom: 6px; +} + +.add-wikibase-dialog textarea { + width: 100%; + height: 160px; +} + +.add-wikibase-dialog .add-wikibase-buttons { + text-align: right; +} + +.add-wikibase-dialog .invalid-manifest { + color: red; +} diff --git a/extensions/wikidata/module/styles/dialogs/import-schema-dialog.less b/extensions/wikidata/module/styles/dialogs/import-schema-dialog.less index 54f4842c7..f76a398ef 100644 --- a/extensions/wikidata/module/styles/dialogs/import-schema-dialog.less +++ b/extensions/wikidata/module/styles/dialogs/import-schema-dialog.less @@ -1,11 +1,11 @@ - @import-less url("../theme.less"); .wikibase-invalid-schema { + margin-top: 10px; color: red; } .wikibase-schema-textarea { width: 100%; - height: 100px; + height: 160px; } diff --git a/extensions/wikidata/module/styles/dialogs/manage-account-dialog.less b/extensions/wikidata/module/styles/dialogs/manage-account-dialog.less index 33449f52c..9c0cfac28 100644 --- a/extensions/wikidata/module/styles/dialogs/manage-account-dialog.less +++ b/extensions/wikidata/module/styles/dialogs/manage-account-dialog.less @@ -75,11 +75,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. bottom: 12px; } - -.wikibase-perform-edits-buttons { - text-align: right; -} - .wikibase-import-schema-buttons { text-align: right; } diff --git a/extensions/wikidata/module/styles/dialogs/perform-edits.less b/extensions/wikidata/module/styles/dialogs/perform-edits.less index 8ff3d4f77..b1349e300 100644 --- a/extensions/wikidata/module/styles/dialogs/perform-edits.less +++ b/extensions/wikidata/module/styles/dialogs/perform-edits.less @@ -33,7 +33,29 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. @import-less url("../theme.less"); +.perform-edits-warnings-area { + margin-bottom: 10px; + min-height: 340px; + max-height: 400px; + overflow-x: hidden; + overflow-y: auto; + border: 1px solid #bbb; +} + +.wikibase-perform-edits-area table { + width: 100%; +} + +.wikibase-perform-edits-area tr td { + padding-top: 6px; +} + .edit-summary { width: 300px; } +.wikibase-perform-edits-buttons { + margin-top: 6px; + text-align: right; +} + diff --git a/extensions/wikidata/module/styles/dialogs/wikibase-dialog.less b/extensions/wikidata/module/styles/dialogs/wikibase-dialog.less new file mode 100644 index 000000000..00e9259c1 --- /dev/null +++ b/extensions/wikidata/module/styles/dialogs/wikibase-dialog.less @@ -0,0 +1,44 @@ +@import-less url("../theme.less"); + +.wikibase-dialog p { + padding: 0 10px 10px 10px +} + +.wikibase-dialog .add-wikibase-buttons { + text-align: right; +} + +.wikibase-list-wrapper { + max-height: 300px; overflow-y: auto +} + +.wikibase-dialog-wikibase-list { + width: 100% +} + +.wikibase-dialog-wikibase-list tr:hover { + background: @dialog_footer; + cursor: pointer; +} + +.wikibase-dialog-wikibase-list tr td { + padding: 10px; +} + +.wikibase-dialog-selector-remove { + float: right; + position: relative; + width: 12px; + height: 12px; + text-decoration: none; + background-image: url(../../images/close-map.png); + background-repeat: no-repeat; + background-position: 0px 0px; +} +.wikibase-dialog-selector-remove:hover { + background-position: -12px 0px; +} + +.wikibase-dialog-selector-remove.wikibase-selected:hover { + cursor: not-allowed; +} diff --git a/extensions/wikidata/module/styles/dialogs/schema-alignment-dialog.css b/extensions/wikidata/module/styles/schema-alignment.less similarity index 96% rename from extensions/wikidata/module/styles/dialogs/schema-alignment-dialog.css rename to extensions/wikidata/module/styles/schema-alignment.less index 11ba5960c..c733c0560 100644 --- a/extensions/wikidata/module/styles/dialogs/schema-alignment-dialog.css +++ b/extensions/wikidata/module/styles/schema-alignment.less @@ -6,7 +6,7 @@ position: initial; } -#wikidata-schema-panel, #wikidata-issues-panel, #wikidata-preview-panel { +#wikibase-schema-panel, #wikibase-issues-panel, #wikibase-preview-panel { display: block; overflow: hidden; height: 100%; @@ -60,6 +60,10 @@ margin: 1em; } +.invalid-schema-warning { + margin: 1em; +} + .schema-alignment-save { float: right; padding: 1em; @@ -465,12 +469,12 @@ } .schema-alignment-columns-header { - margin-bottom: 0.3em; + margin-bottom: 0.3em; } /*** Warnings rendering ****/ -#wikidata-issues-panel table { +#wikibase-issues-panel table { width: 100%; } @@ -503,19 +507,19 @@ tr.wb-warning:nth-of-type(odd) { } .wb-warning-severity-INFO { - background-image: url('../../images/Information.png'); + background-image: url('../images/Information.png'); } .wb-warning-severity-WARNING { - background-image: url('../../images/Warning.png'); + background-image: url('../images/Warning.png'); } .wb-warning-severity-IMPORTANT { - background-image: url('../../images/Important.png'); + background-image: url('../images/Important.png'); } .wb-warning-severity-CRITICAL { - background-image: url('../../images/Critical.png'); + background-image: url('../images/Critical.png'); } .wb-warning-body { @@ -530,15 +534,7 @@ tr.wb-warning:nth-of-type(odd) { } .wb-warning-end { - float: clear; -} - -div.perform-edits-warnings-area { - min-height: 340px; - max-height: 400px; - overflow-x: hidden; - overflow-y: auto; - border: 1px solid #bbb; + float: unset; } .wb-issue-preformat { @@ -568,4 +564,4 @@ div.schema-alignment-dialog-preview { .schema-alignment-dialog-columns-area div:hover{ cursor: grabbing; -} \ No newline at end of file +} diff --git a/extensions/wikidata/pom.xml b/extensions/wikidata/pom.xml index 04a63e961..337610b53 100644 --- a/extensions/wikidata/pom.xml +++ b/extensions/wikidata/pom.xml @@ -175,7 +175,13 @@ powermock-api-mockito2 ${powermock.version} test - + + + com.squareup.okhttp3 + mockwebserver + 4.8.0 + test + diff --git a/extensions/wikidata/src/org/openrefine/wikidata/commands/ConnectionManager.java b/extensions/wikidata/src/org/openrefine/wikidata/commands/ConnectionManager.java index 7f5827471..2c681a32a 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/commands/ConnectionManager.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/commands/ConnectionManager.java @@ -43,13 +43,10 @@ import java.util.List; import java.util.Map; /** - * Manages a connection to Wikidata. + * Manages a connection to the current Wikibase instance. *

* The connection can be either {@link BasicApiConnection} or {@link OAuthApiConnection}. *

- * This class is also hard-coded for Wikidata, - * it will be generalized to other Wikibase instances soon. - * * @author Antonin Delpeuch * @author Lu Liu */ @@ -67,19 +64,7 @@ public class ConnectionManager { public static final int CONNECT_TIMEOUT = 5000; public static final int READ_TIMEOUT = 10000; - /** - * For now, this class is hard-coded for Wikidata. - *

- * It will be generalized to work against other Wikibase instances in the future. - */ - private static final String WIKIBASE_API_ENDPOINT = ApiConnection.URL_WIKIDATA_API; - - /** - * The single {@link ApiConnection} instance managed by {@link ConnectionManager}. - *

- * Currently, only one connection is supported at the same time. - */ - private ApiConnection connection; + private Map endpointToConnection = new HashMap<>(); private static final ConnectionManager instance = new ConnectionManager(); @@ -98,19 +83,20 @@ public class ConnectionManager { *

* If failed to login, the connection will be set to null. * + * @param mediaWikiApiEndpoint the api endpoint of the target Wikibase instance * @param username the username to log in with * @param password the password to log in with * @return true if logged in successfully, false otherwise */ - public boolean login(String username, String password) { - connection = new BasicApiConnection(WIKIBASE_API_ENDPOINT); + public boolean login(String mediaWikiApiEndpoint, String username, String password) { + BasicApiConnection connection = new BasicApiConnection(mediaWikiApiEndpoint); setupConnection(connection); try { - ((BasicApiConnection) connection).login(username, password); + connection.login(username, password); + endpointToConnection.put(mediaWikiApiEndpoint, connection); return true; } catch (LoginFailedException e) { - logger.error(e.getMessage()); - connection = null; + logger.error(e.getMessage(), e); return false; } } @@ -120,25 +106,26 @@ public class ConnectionManager { *

* If failed to login, the connection will be set to null. * + * @param mediaWikiApiEndpoint the api endpoint of the target Wikibase instance * @param consumerToken consumer token of an owner-only consumer * @param consumerSecret consumer secret of an owner-only consumer * @param accessToken access token of an owner-only consumer * @param accessSecret access secret of an owner-only consumer * @return true if logged in successfully, false otherwise */ - public boolean login(String consumerToken, String consumerSecret, + public boolean login(String mediaWikiApiEndpoint, String consumerToken, String consumerSecret, String accessToken, String accessSecret) { - connection = new OAuthApiConnection(WIKIBASE_API_ENDPOINT, + OAuthApiConnection connection = new OAuthApiConnection(mediaWikiApiEndpoint, consumerToken, consumerSecret, accessToken, accessSecret); setupConnection(connection); try { // check if the credentials are valid connection.checkCredentials(); + endpointToConnection.put(mediaWikiApiEndpoint, connection); return true; } catch (IOException | MediaWikiApiErrorException e) { - logger.error(e.getMessage()); - connection = null; + logger.error(e.getMessage(), e); return false; } } @@ -149,14 +136,15 @@ public class ConnectionManager { *

* If failed to login, the connection will be set to null. * + * @param mediaWikiApiEndpoint the api endpoint of the target Wikibase instance * @param username the username * @param cookies the cookies used to login * @return true if logged in successfully, false otherwise */ - public boolean login(String username, List cookies) { + public boolean login(String mediaWikiApiEndpoint, String username, List cookies) { cookies.forEach(cookie -> cookie.setPath("/")); Map map = new HashMap<>(); - map.put("baseUrl", WIKIBASE_API_ENDPOINT); + map.put("baseUrl", mediaWikiApiEndpoint); map.put("cookies", cookies); map.put("username", username); map.put("loggedIn", true); @@ -164,13 +152,12 @@ public class ConnectionManager { map.put("connectTimeout", CONNECT_TIMEOUT); map.put("readTimeout", READ_TIMEOUT); try { - BasicApiConnection newConnection = convertToBasicApiConnection(map); - newConnection.checkCredentials(); - connection = newConnection; + BasicApiConnection connection = convertToBasicApiConnection(map); + connection.checkCredentials(); + endpointToConnection.put(mediaWikiApiEndpoint, connection); return true; } catch (IOException | MediaWikiApiErrorException e) { - logger.error(e.getMessage()); - connection = null; + logger.error(e.getMessage(), e); return false; } } @@ -185,33 +172,35 @@ public class ConnectionManager { } - public void logout() { + public void logout(String mediaWikiApiEndpoint) { + ApiConnection connection = endpointToConnection.get(mediaWikiApiEndpoint); if (connection != null) { try { connection.logout(); - connection = null; + endpointToConnection.remove(mediaWikiApiEndpoint); } catch (IOException e) { - logger.error(e.getMessage()); + logger.error(e.getMessage(), e); } catch (MediaWikiApiErrorException e) { if ("assertuserfailed".equals(e.getErrorCode())) { // it turns out we were already logged out - connection = null; + endpointToConnection.remove(mediaWikiApiEndpoint); } else { - logger.error(e.getMessage()); + logger.error(e.getMessage(), e); } } } } - public ApiConnection getConnection() { - return connection; + public ApiConnection getConnection(String mediaWikiApiEndpoint) { + return endpointToConnection.get(mediaWikiApiEndpoint); } - public boolean isLoggedIn() { - return connection != null; + public boolean isLoggedIn(String mediaWikiApiEndpoint) { + return endpointToConnection.get(mediaWikiApiEndpoint) != null; } - public String getUsername() { + public String getUsername(String mediaWikiApiEndpoint) { + ApiConnection connection = endpointToConnection.get(mediaWikiApiEndpoint); if (connection != null) { return connection.getCurrentUser(); } else { diff --git a/extensions/wikidata/src/org/openrefine/wikidata/commands/LoginCommand.java b/extensions/wikidata/src/org/openrefine/wikidata/commands/LoginCommand.java index 04ef78ba8..3efeb9a87 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/commands/LoginCommand.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/commands/LoginCommand.java @@ -50,12 +50,15 @@ import static org.apache.commons.lang.StringUtils.isNotBlank; * Both logging in with username/password or owner-only consumer are supported. *

* This command also manages cookies of login credentials. + *

+ * Cookies for different MediaWiki API endpoint are stored, + * but only one connection is kept at the same time. */ public class LoginCommand extends Command { - static final String WIKIDATA_COOKIE_PREFIX = "openrefine-wikidata-"; + static final String WIKIBASE_COOKIE_PREFIX = "openrefine-wikibase-"; - static final String WIKIBASE_USERNAME_COOKIE_KEY = "wikibase-username"; + static final String API_ENDPOINT = "wb-api-endpoint"; static final String USERNAME = "wb-username"; static final String PASSWORD = "wb-password"; @@ -75,10 +78,17 @@ public class LoginCommand extends Command { ConnectionManager manager = ConnectionManager.getInstance(); + String mediawikiApiEndpoint = removeCRLF(request.getParameter(API_ENDPOINT)); + if (isBlank(mediawikiApiEndpoint)) { + CommandUtilities.respondError(response, "missing parameter '" + API_ENDPOINT + "'"); + return; + } + String mediawikiApiEndpointPrefix = mediawikiApiEndpoint + '-'; + if ("true".equals(request.getParameter("logout"))) { - manager.logout(); - removeUsernamePasswordCookies(request, response); - removeOwnerOnlyConsumerCookies(request, response); + manager.logout(mediawikiApiEndpoint); + removeUsernamePasswordCookies(mediawikiApiEndpointPrefix, request, response); + removeOwnerOnlyConsumerCookies(mediawikiApiEndpointPrefix, request, response); respond(request, response); return; // return directly } @@ -93,53 +103,35 @@ public class LoginCommand extends Command { String accessToken = request.getParameter(ACCESS_TOKEN); String accessSecret = request.getParameter(ACCESS_SECRET); - if (isBlank(username) && isBlank(password) && isBlank(consumerToken) && - isBlank(consumerSecret) && isBlank(accessToken) && isBlank(accessSecret)) { - // In this case, we use cookie to login, and we will always remember the credentials in cookies. + if (isBlank(username) && isBlank(password) && isBlank(consumerToken) + && isBlank(consumerSecret) && isBlank(accessToken) && isBlank(accessSecret)) { + // In this case, we use cookies to login, and we will always remember the credentials in cookies. remember = true; - Cookie[] cookies = request.getCookies(); - - for (Cookie cookie : cookies) { - String value = getCookieValue(cookie); - switch (cookie.getName()) { - case CONSUMER_TOKEN: - consumerToken = value; - break; - case CONSUMER_SECRET: - consumerSecret = value; - break; - case ACCESS_TOKEN: - accessToken = value; - break; - case ACCESS_SECRET: - accessSecret = value; - break; - default: - break; - } - } + Map cookieMap = processCookiesWithPrefix(mediawikiApiEndpointPrefix, request.getCookies()); + username = cookieMap.get(USERNAME); + consumerToken = cookieMap.get(CONSUMER_TOKEN); + consumerSecret = cookieMap.get(CONSUMER_SECRET); + accessToken = cookieMap.get(ACCESS_TOKEN); + accessSecret = cookieMap.get(ACCESS_SECRET); if (isBlank(consumerToken) && isBlank(consumerSecret) && isBlank(accessToken) && isBlank(accessSecret)) { // Try logging in with the cookies of a password-based connection. - String username1 = null; List cookieList = new ArrayList<>(); - for (Cookie cookie : cookies) { - if (cookie.getName().startsWith(WIKIDATA_COOKIE_PREFIX)) { - String cookieName = cookie.getName().substring(WIKIDATA_COOKIE_PREFIX.length()); - Cookie newCookie = new Cookie(cookieName, getCookieValue(cookie)); + for (Map.Entry entry : cookieMap.entrySet()) { + if (entry.getKey().startsWith(WIKIBASE_COOKIE_PREFIX)) { + String name = entry.getKey().substring(WIKIBASE_COOKIE_PREFIX.length()); + Cookie newCookie = new Cookie(name, entry.getValue()); cookieList.add(newCookie); - } else if (cookie.getName().equals(WIKIBASE_USERNAME_COOKIE_KEY)) { - username1 = getCookieValue(cookie); } } - if (cookieList.size() > 0 && username1 != null) { - removeOwnerOnlyConsumerCookies(request, response); - if (manager.login(username1, cookieList)) { + if (cookieList.size() > 0 && isNotBlank(username)) { + removeOwnerOnlyConsumerCookies(mediawikiApiEndpointPrefix, request, response); + if (manager.login(mediawikiApiEndpoint, username, cookieList)) { respond(request, response); return; } else { - removeUsernamePasswordCookies(request, response); + removeUsernamePasswordCookies(mediawikiApiEndpointPrefix, request, response); } } } @@ -148,31 +140,32 @@ public class LoginCommand extends Command { if (isNotBlank(username) && isNotBlank(password)) { // Once logged in with new credentials, // the old credentials in cookies should be cleared. - if (manager.login(username, password) && remember) { - ApiConnection connection = manager.getConnection(); + if (manager.login(mediawikiApiEndpoint, username, password) && remember) { + ApiConnection connection = manager.getConnection(mediawikiApiEndpoint); List cookies = ((BasicApiConnection) connection).getCookies(); + String prefix = mediawikiApiEndpointPrefix + WIKIBASE_COOKIE_PREFIX; for (HttpCookie cookie : cookies) { - setCookie(response, WIKIDATA_COOKIE_PREFIX + cookie.getName(), cookie.getValue()); + setCookie(response, prefix + cookie.getName(), cookie.getValue()); } // Though the cookies from the connection contain some cookies of username, // we cannot make sure that all Wikibase instances use the same cookie key // to retrieve the username. So we choose to set the username cookie with our own cookie key. - setCookie(response, WIKIBASE_USERNAME_COOKIE_KEY, connection.getCurrentUser()); + setCookie(response, mediawikiApiEndpointPrefix + USERNAME, connection.getCurrentUser()); } else { - removeUsernamePasswordCookies(request, response); + removeUsernamePasswordCookies(mediawikiApiEndpointPrefix, request, response); } - removeOwnerOnlyConsumerCookies(request, response); + removeOwnerOnlyConsumerCookies(mediawikiApiEndpointPrefix, request, response); } else if (isNotBlank(consumerToken) && isNotBlank(consumerSecret) && isNotBlank(accessToken) && isNotBlank(accessSecret)) { - if (manager.login(consumerToken, consumerSecret, accessToken, accessSecret) && remember) { - setCookie(response, CONSUMER_TOKEN, consumerToken); - setCookie(response, CONSUMER_SECRET, consumerSecret); - setCookie(response, ACCESS_TOKEN, accessToken); - setCookie(response, ACCESS_SECRET, accessSecret); + if (manager.login(mediawikiApiEndpoint, consumerToken, consumerSecret, accessToken, accessSecret) && remember) { + setCookie(response, mediawikiApiEndpointPrefix + CONSUMER_TOKEN, consumerToken); + setCookie(response, mediawikiApiEndpointPrefix + CONSUMER_SECRET, consumerSecret); + setCookie(response, mediawikiApiEndpointPrefix + ACCESS_TOKEN, accessToken); + setCookie(response, mediawikiApiEndpointPrefix + ACCESS_SECRET, accessSecret); } else { - removeOwnerOnlyConsumerCookies(request, response); + removeOwnerOnlyConsumerCookies(mediawikiApiEndpointPrefix, request, response); } - removeUsernamePasswordCookies(request, response); + removeUsernamePasswordCookies(mediawikiApiEndpointPrefix, request, response); } respond(request, response); @@ -185,28 +178,60 @@ public class LoginCommand extends Command { } protected void respond(HttpServletRequest request, HttpServletResponse response) throws IOException { + String mediawikiApiEndpoint = request.getParameter(API_ENDPOINT); + if (isBlank(mediawikiApiEndpoint)) { + CommandUtilities.respondError(response, "missing parameter '" + API_ENDPOINT + "'"); + return; + } + ConnectionManager manager = ConnectionManager.getInstance(); Map jsonResponse = new HashMap<>(); - jsonResponse.put("logged_in", manager.isLoggedIn()); - jsonResponse.put("username", manager.getUsername()); + if (manager.isLoggedIn(mediawikiApiEndpoint)) { + jsonResponse.put("logged_in", manager.isLoggedIn(mediawikiApiEndpoint)); + jsonResponse.put("username", manager.getUsername(mediawikiApiEndpoint)); + jsonResponse.put("mediawiki_api_endpoint", mediawikiApiEndpoint); + } else { + jsonResponse.put("logged_in", false); + jsonResponse.put("username", null); + jsonResponse.put("mediawiki_api_endpoint", mediawikiApiEndpoint); + } + respondJSON(response, jsonResponse); } - private static void removeUsernamePasswordCookies(HttpServletRequest request, HttpServletResponse response) { + + /** + * 1. Filters cookies with the given prefix + * 2. Removes the prefix + */ + private static Map processCookiesWithPrefix(String prefix, Cookie[] cookies) throws UnsupportedEncodingException { + Map result = new HashMap<>(); + for (Cookie cookie : cookies) { + String name = cookie.getName(); + if (name.startsWith(prefix)) { + result.put(name.substring(prefix.length()), getCookieValue(cookie)); + } + } + + return result; + } + + private static void removeUsernamePasswordCookies(String wikibaseApiEndpointPrefix, HttpServletRequest request, HttpServletResponse response) { + String toRemovePrefix = wikibaseApiEndpointPrefix + WIKIBASE_COOKIE_PREFIX; Cookie[] cookies = request.getCookies(); for (Cookie cookie : cookies) { - if (cookie.getName().startsWith(WIKIDATA_COOKIE_PREFIX)) { + if (cookie.getName().startsWith(toRemovePrefix)) { removeCookie(response, cookie.getName()); } } - removeCookie(response, WIKIBASE_USERNAME_COOKIE_KEY); + removeCookie(response, wikibaseApiEndpointPrefix + USERNAME); } - private static void removeOwnerOnlyConsumerCookies(HttpServletRequest request, HttpServletResponse response) { - removeCookie(response, CONSUMER_TOKEN); - removeCookie(response, CONSUMER_SECRET); - removeCookie(response, ACCESS_TOKEN); - removeCookie(response, ACCESS_SECRET); + private static void removeOwnerOnlyConsumerCookies(String wikibaseApiEndpointPrefix, HttpServletRequest request, HttpServletResponse response) { + removeCookie(response, wikibaseApiEndpointPrefix + CONSUMER_TOKEN); + removeCookie(response, wikibaseApiEndpointPrefix + CONSUMER_SECRET); + removeCookie(response, wikibaseApiEndpointPrefix + ACCESS_TOKEN); + removeCookie(response, wikibaseApiEndpointPrefix + ACCESS_SECRET); } static String getCookieValue(Cookie cookie) throws UnsupportedEncodingException { @@ -230,4 +255,17 @@ public class LoginCommand extends Command { cookie.setSecure(false); response.addCookie(cookie); } + + /** + * To avoid HTTP response splitting. + * + * See https://lgtm.com/rules/3980077/ + */ + static String removeCRLF(String str) { + if (str == null) { + return ""; + } else { + return str.replaceAll("[\n\r]", ""); + } + } } diff --git a/extensions/wikidata/src/org/openrefine/wikidata/commands/PerformWikibaseEditsCommand.java b/extensions/wikidata/src/org/openrefine/wikidata/commands/PerformWikibaseEditsCommand.java index 94925d97f..a0490d04e 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/commands/PerformWikibaseEditsCommand.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/commands/PerformWikibaseEditsCommand.java @@ -38,7 +38,9 @@ public class PerformWikibaseEditsCommand extends EngineDependentCommand { protected AbstractOperation createOperation(Project project, HttpServletRequest request, EngineConfig engineConfig) throws Exception { String summary = request.getParameter("summary"); - return new PerformWikibaseEditsOperation(engineConfig, summary); + String maxlagStr = request.getParameter("maxlag"); + int maxlag = maxlagStr == null ? 5 : Integer.parseInt(maxlagStr); + return new PerformWikibaseEditsOperation(engineConfig, summary, maxlag); } } diff --git a/extensions/wikidata/src/org/openrefine/wikidata/commands/PreviewWikibaseSchemaCommand.java b/extensions/wikidata/src/org/openrefine/wikidata/commands/PreviewWikibaseSchemaCommand.java index 1ebdfb1dd..ef806551b 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/commands/PreviewWikibaseSchemaCommand.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/commands/PreviewWikibaseSchemaCommand.java @@ -1,18 +1,18 @@ /******************************************************************************* * MIT License - * + * * Copyright (c) 2018 Antonin Delpeuch - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. - * + * * 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 @@ -34,6 +34,9 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.openrefine.wikidata.manifests.Manifest; +import org.openrefine.wikidata.manifests.ManifestException; +import org.openrefine.wikidata.manifests.ManifestParser; import org.openrefine.wikidata.qa.EditInspector; import org.openrefine.wikidata.qa.QAWarningStore; import org.openrefine.wikidata.schema.WikibaseSchema; @@ -63,12 +66,12 @@ public class PreviewWikibaseSchemaCommand extends Command { response.setCharacterEncoding("UTF-8"); response.setHeader("Content-Type", "application/json"); - String jsonString = request.getParameter("schema"); + String schemaJson = request.getParameter("schema"); WikibaseSchema schema = null; - if (jsonString != null) { + if (schemaJson != null) { try { - schema = WikibaseSchema.reconstruct(jsonString); + schema = WikibaseSchema.reconstruct(schemaJson); } catch (IOException e) { respondError(response, "Wikibase schema could not be parsed."); return; @@ -81,6 +84,21 @@ public class PreviewWikibaseSchemaCommand extends Command { return; } + Manifest manifest = null; + String manifestJson = request.getParameter("manifest"); + if (manifestJson != null) { + try { + manifest = ManifestParser.parse(manifestJson); + } catch (ManifestException e) { + respondError(response, "Wikibase manifest could not be parsed. Error message: " + e.getMessage()); + return; + } + } + if (manifest == null) { + respondError(response, "No Wikibase manifest provided."); + return; + } + QAWarningStore warningStore = new QAWarningStore(); // Evaluate project @@ -88,9 +106,9 @@ public class PreviewWikibaseSchemaCommand extends Command { List editBatch = schema.evaluate(project, engine, warningStore); // Inspect the edits and generate warnings - EditInspector inspector = new EditInspector(warningStore); + EditInspector inspector = new EditInspector(warningStore, manifest); inspector.inspect(editBatch); - + // Dump the first 10 edits, scheduled with the default scheduler WikibaseAPIUpdateScheduler scheduler = new WikibaseAPIUpdateScheduler(); List nonNullEdits = scheduler.schedule(editBatch).stream() diff --git a/extensions/wikidata/src/org/openrefine/wikidata/editing/EditBatchProcessor.java b/extensions/wikidata/src/org/openrefine/wikidata/editing/EditBatchProcessor.java index 5a183bd01..241517027 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/editing/EditBatchProcessor.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/editing/EditBatchProcessor.java @@ -43,8 +43,6 @@ import org.wikidata.wdtk.datamodel.interfaces.MonolingualTextValue; import org.wikidata.wdtk.wikibaseapi.WikibaseDataEditor; import org.wikidata.wdtk.wikibaseapi.WikibaseDataFetcher; import org.wikidata.wdtk.wikibaseapi.apierrors.MediaWikiApiErrorException; -import com.google.refine.ProjectManager; -import com.google.refine.preference.PreferenceStore; /** @@ -70,17 +68,6 @@ public class EditBatchProcessor { private int globalCursor; private Map currentDocs; private int batchSize; - protected static final String MAX_LAG_KEY = "wikibase.upload.maxLag"; - protected static final int MAX_LAG_DEFAULT = 5; // 5 second default maxLag - protected PreferenceStore prefStore = ProjectManager.singleton.getPreferenceStore(); - - private int getMaxLag() { - try { - return Integer.parseInt((String) prefStore.get(MAX_LAG_KEY)); - } catch (NumberFormatException e) { - return MAX_LAG_DEFAULT; - } - } /** * Initiates the process of pushing a batch of updates to Wikibase. This @@ -104,7 +91,7 @@ public class EditBatchProcessor { * API */ public EditBatchProcessor(WikibaseDataFetcher fetcher, WikibaseDataEditor editor, List updates, - NewItemLibrary library, String summary, List tags, int batchSize) { + NewItemLibrary library, String summary, int maxLag, List tags, int batchSize) { this.fetcher = fetcher; this.editor = editor; editor.setEditAsBot(true); // this will not do anything if the user does not @@ -114,7 +101,6 @@ public class EditBatchProcessor { // it will slow us down via the maxlag mechanism. editor.setAverageTimePerEdit(1000); // set maxlag based on preference store - int maxLag = getMaxLag(); editor.setMaxLag(maxLag); this.library = library; diff --git a/extensions/wikidata/src/org/openrefine/wikidata/editing/WikibaseCredentials.java b/extensions/wikidata/src/org/openrefine/wikidata/editing/WikibaseCredentials.java deleted file mode 100644 index 4c27adadc..000000000 --- a/extensions/wikidata/src/org/openrefine/wikidata/editing/WikibaseCredentials.java +++ /dev/null @@ -1,75 +0,0 @@ -/******************************************************************************* - * MIT License - * - * Copyright (c) 2018 Antonin Delpeuch - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * 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 OR COPYRIGHT HOLDERS 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. - ******************************************************************************/ -package org.openrefine.wikidata.editing; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; - - -/** - * This is just the necessary bits to store Wikidata credentials in OpenRefine's - * preference store. - * - * @author Antonin Delpeuch - * - */ -class WikibaseCredentials { - - @JsonProperty("username") - private String username; - @JsonProperty("password") - private String password; - - public WikibaseCredentials() { - username = null; - password = null; - } - - @JsonCreator - public WikibaseCredentials( - @JsonProperty("username") - String username, - @JsonProperty("password") - String password) { - this.username = username; - this.password = password; - } - - public String getUsername() { - return username; - } - - public String getPassword() { - return password; - } - - public boolean isNonNull() { - return username != null && password != null && !"null".equals(username) && !"null".equals(password); - } - - @JsonProperty("class") - public String getClassName() { - return getClass().getName(); - } -} diff --git a/extensions/wikidata/src/org/openrefine/wikidata/manifests/Manifest.java b/extensions/wikidata/src/org/openrefine/wikidata/manifests/Manifest.java new file mode 100644 index 000000000..215041192 --- /dev/null +++ b/extensions/wikidata/src/org/openrefine/wikidata/manifests/Manifest.java @@ -0,0 +1,23 @@ +package org.openrefine.wikidata.manifests; + +public interface Manifest { + + String getVersion(); + + String getName(); + + String getSiteIri(); + + int getMaxlag(); + + String getInstanceOfPid(); + + String getSubclassOfPid(); + + String getMediaWikiApiEndpoint(); + + String getReconServiceEndpoint(); + + String getConstraintsRelatedId(String name); + +} diff --git a/extensions/wikidata/src/org/openrefine/wikidata/manifests/ManifestException.java b/extensions/wikidata/src/org/openrefine/wikidata/manifests/ManifestException.java new file mode 100644 index 000000000..a1765cf1c --- /dev/null +++ b/extensions/wikidata/src/org/openrefine/wikidata/manifests/ManifestException.java @@ -0,0 +1,12 @@ +package org.openrefine.wikidata.manifests; + +public class ManifestException extends Exception { + + public ManifestException(String msg) { + super(msg); + } + + public ManifestException(String msg, Throwable cause) { + super(msg, cause); + } +} diff --git a/extensions/wikidata/src/org/openrefine/wikidata/manifests/ManifestParser.java b/extensions/wikidata/src/org/openrefine/wikidata/manifests/ManifestParser.java new file mode 100644 index 000000000..22208ac28 --- /dev/null +++ b/extensions/wikidata/src/org/openrefine/wikidata/manifests/ManifestParser.java @@ -0,0 +1,40 @@ +package org.openrefine.wikidata.manifests; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public final class ManifestParser { + + private static final Logger logger = LoggerFactory.getLogger(ManifestParser.class); + + private static final ObjectMapper mapper = new ObjectMapper(); + + public static Manifest parse(String manifestJson) throws ManifestException { + JsonNode root; + try { + root = mapper.readTree(manifestJson); + } catch (JsonProcessingException e) { + throw new ManifestException("invalid manifest format", e); + } + + String version = root.path("version").textValue(); + if (StringUtils.isBlank(version)) { + throw new ManifestException("invalid manifest format, version is missing"); + } + if (!version.matches("[0-9]+\\.[0-9]+")) { + throw new ManifestException("invalid version: " + version); + } + + String majorVersion = version.split("\\.")[0]; + // support only v1.x for now + if ("1".equals(majorVersion)) { + return new ManifestV1(root); + } else { + throw new ManifestException("unsupported manifest version: " + version); + } + } +} diff --git a/extensions/wikidata/src/org/openrefine/wikidata/manifests/ManifestV1.java b/extensions/wikidata/src/org/openrefine/wikidata/manifests/ManifestV1.java new file mode 100644 index 000000000..f14e9a82e --- /dev/null +++ b/extensions/wikidata/src/org/openrefine/wikidata/manifests/ManifestV1.java @@ -0,0 +1,94 @@ +package org.openrefine.wikidata.manifests; + +import com.fasterxml.jackson.databind.JsonNode; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +public class ManifestV1 implements Manifest { + + private String version; + private String name; + private String siteIri; + private int maxlag; + private String instanceOfPid; + private String subclassOfPid; + private String mediaWikiApiEndpoint; + private String reconServiceEndpoint; + + private Map constraintsRelatedIdMap = new HashMap<>(); + + public ManifestV1(JsonNode manifest) { + version = manifest.path("version").textValue(); + + JsonNode mediawiki = manifest.path("mediawiki"); + name = mediawiki.path("name").textValue(); + mediaWikiApiEndpoint = mediawiki.path("api").textValue(); + + JsonNode wikibase = manifest.path("wikibase"); + siteIri = wikibase.path("site_iri").textValue(); + maxlag = wikibase.path("maxlag").intValue(); + JsonNode properties = wikibase.path("properties"); + instanceOfPid = properties.path("instance_of").textValue(); + subclassOfPid = properties.path("subclass_of").textValue(); + + JsonNode constraints = wikibase.path("constraints"); + Iterator> fields = constraints.fields(); + while (fields.hasNext()) { + Map.Entry entry = fields.next(); + String name = entry.getKey(); + String value = entry.getValue().textValue(); + constraintsRelatedIdMap.put(name, value); + } + + JsonNode reconciliation = manifest.path("reconciliation"); + reconServiceEndpoint = reconciliation.path("endpoint").textValue(); + } + + @Override + public String getVersion() { + return version; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getSiteIri() { + return siteIri; + } + + @Override + public int getMaxlag() { + return maxlag; + } + + @Override + public String getInstanceOfPid() { + return instanceOfPid; + } + + @Override + public String getSubclassOfPid() { + return subclassOfPid; + } + + @Override + public String getMediaWikiApiEndpoint() { + return mediaWikiApiEndpoint; + } + + @Override + public String getReconServiceEndpoint() { + return reconServiceEndpoint; + } + + @Override + public String getConstraintsRelatedId(String name) { + return constraintsRelatedIdMap.get(name); + } + +} diff --git a/extensions/wikidata/src/org/openrefine/wikidata/operations/PerformWikibaseEditsOperation.java b/extensions/wikidata/src/org/openrefine/wikidata/operations/PerformWikibaseEditsOperation.java index 50a573cc1..f8c17b85e 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/operations/PerformWikibaseEditsOperation.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/operations/PerformWikibaseEditsOperation.java @@ -34,6 +34,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.lang.Validate; +import org.apache.commons.lang3.StringUtils; import org.openrefine.wikidata.commands.ConnectionManager; import org.openrefine.wikidata.editing.EditBatchProcessor; import org.openrefine.wikidata.editing.NewItemLibrary; @@ -67,16 +68,27 @@ public class PerformWikibaseEditsOperation extends EngineDependentOperation { @JsonProperty("summary") private String summary; + @JsonProperty("maxlag") + private int maxlag; + @JsonCreator public PerformWikibaseEditsOperation( @JsonProperty("engineConfig") EngineConfig engineConfig, @JsonProperty("summary") - String summary) { + String summary, + @JsonProperty("maxlag") + Integer maxlag) { super(engineConfig); Validate.notNull(summary, "An edit summary must be provided."); Validate.notEmpty(summary, "An edit summary must be provided."); this.summary = summary; + if (maxlag == null) { + // For backward compatibility, if the maxlag parameter is not included + // in the serialized JSON text, set it to 5. + maxlag = 5; + } + this.maxlag = maxlag; } @Override @@ -171,23 +183,28 @@ public class PerformWikibaseEditsOperation extends EngineDependentOperation { WebResourceFetcherImpl.setUserAgent("OpenRefine Wikidata extension"); ConnectionManager manager = ConnectionManager.getInstance(); - if (!manager.isLoggedIn()) { + String mediaWikiApiEndpoint = _schema.getMediaWikiApiEndpoint(); + if (!manager.isLoggedIn(mediaWikiApiEndpoint)) { return; } - ApiConnection connection = manager.getConnection(); + ApiConnection connection = manager.getConnection(mediaWikiApiEndpoint); - WikibaseDataFetcher wbdf = new WikibaseDataFetcher(connection, _schema.getBaseIri()); - WikibaseDataEditor wbde = new WikibaseDataEditor(connection, _schema.getBaseIri()); - - // Generate batch token - long token = (new Random()).nextLong(); - // The following replacement is a fix for: https://github.com/Wikidata/editgroups/issues/4 - // Because commas and colons are used by Wikibase to separate the auto-generated summaries - // from the user-supplied ones, we replace these separators by similar unicode characters to - // make sure they can be told apart. - String summaryWithoutCommas = _summary.replaceAll(", ","ꓹ ").replaceAll(": ","։ "); - String summary = summaryWithoutCommas + String.format(" ([[:toollabs:editgroups/b/OR/%s|details]])", - (Long.toHexString(token).substring(0, 11))); + WikibaseDataFetcher wbdf = new WikibaseDataFetcher(connection, _schema.getSiteIri()); + WikibaseDataEditor wbde = new WikibaseDataEditor(connection, _schema.getSiteIri()); + + String summary; + if (StringUtils.isBlank(_schema.getEditGroupsURLSchema())) { + summary = _summary; + } else { + // Generate batch id + String batchId = Long.toHexString((new Random()).nextLong()).substring(0, 11); + // The following replacement is a fix for: https://github.com/Wikidata/editgroups/issues/4 + // Because commas and colons are used by Wikibase to separate the auto-generated summaries + // from the user-supplied ones, we replace these separators by similar unicode characters to + // make sure they can be told apart. + String summaryWithoutCommas = _summary.replaceAll(", ","ꓹ ").replaceAll(": ","։ "); + summary = summaryWithoutCommas + " " + _schema.getEditGroupsURLSchema().replace("${batch_id}", batchId); + } // Evaluate the schema List itemDocuments = _schema.evaluate(_project, _engine); @@ -195,7 +212,7 @@ public class PerformWikibaseEditsOperation extends EngineDependentOperation { // Prepare the edits NewItemLibrary newItemLibrary = new NewItemLibrary(); EditBatchProcessor processor = new EditBatchProcessor(wbdf, wbde, itemDocuments, newItemLibrary, summary, - _tags, 50); + maxlag, _tags, 50); // Perform edits logger.info("Performing edits"); diff --git a/extensions/wikidata/src/org/openrefine/wikidata/qa/ConstraintFetcher.java b/extensions/wikidata/src/org/openrefine/wikidata/qa/ConstraintFetcher.java index 99c157296..8099220f3 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/qa/ConstraintFetcher.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/qa/ConstraintFetcher.java @@ -23,18 +23,36 @@ ******************************************************************************/ package org.openrefine.wikidata.qa; +import org.openrefine.wikidata.utils.EntityCache; +import org.wikidata.wdtk.datamodel.interfaces.EntityIdValue; +import org.wikidata.wdtk.datamodel.interfaces.PropertyDocument; import org.wikidata.wdtk.datamodel.interfaces.PropertyIdValue; import org.wikidata.wdtk.datamodel.interfaces.Statement; +import org.wikidata.wdtk.datamodel.interfaces.StatementGroup; +import org.wikidata.wdtk.datamodel.interfaces.StatementRank; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; /** - * An object that fetches constraints about properties. + * This class provides an abstraction over the way constraint definitions are + * stored in a Wikibase instance. * * @author Antonin Delpeuch * */ -public interface ConstraintFetcher { +public class ConstraintFetcher { + + private String wikibaseConstraintPid; + + private EntityCache entityCache; + + public ConstraintFetcher(EntityCache cache, String wikibaseConstraintPid) { + entityCache = cache; + this.wikibaseConstraintPid = wikibaseConstraintPid; + } /** * Gets the list of constraints of a particular type for a property @@ -45,6 +63,30 @@ public interface ConstraintFetcher { * the type of the constraints * @return the list of matching constraint statements */ - List getConstraintsByType(PropertyIdValue pid, String qid); + public List getConstraintsByType(PropertyIdValue pid, String qid) { + Stream allConstraints = getConstraintStatements(pid).stream() + .filter(s -> s.getValue() != null && ((EntityIdValue) s.getValue()).getId().equals(qid)) + .filter(s -> !StatementRank.DEPRECATED.equals(s.getRank())); + return allConstraints.collect(Collectors.toList()); + } + + /** + * Gets all the constraint statements for a given property + * + * @param pid + * the id of the property to retrieve the constraints for + * @return the list of constraint statements + */ + private List getConstraintStatements(PropertyIdValue pid) { + PropertyDocument doc = (PropertyDocument) entityCache.get(pid); + StatementGroup group = doc.findStatementGroup(wikibaseConstraintPid); + if (group != null) { + return group.getStatements().stream() + .filter(s -> s.getValue() != null && s.getValue() instanceof EntityIdValue) + .collect(Collectors.toList()); + } else { + return Collections.emptyList(); + } + } } diff --git a/extensions/wikidata/src/org/openrefine/wikidata/qa/EditInspector.java b/extensions/wikidata/src/org/openrefine/wikidata/qa/EditInspector.java index 119e7b90b..94a66f511 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/qa/EditInspector.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/qa/EditInspector.java @@ -1,18 +1,18 @@ /******************************************************************************* * MIT License - * + * * Copyright (c) 2018 Antonin Delpeuch - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. - * + * * 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 @@ -23,10 +23,13 @@ ******************************************************************************/ package org.openrefine.wikidata.qa; +import org.openrefine.wikidata.manifests.Manifest; import org.openrefine.wikidata.qa.scrutinizers.*; import org.openrefine.wikidata.updates.ItemUpdate; import org.openrefine.wikidata.updates.scheduler.WikibaseAPIUpdateScheduler; import org.openrefine.wikidata.utils.EntityCache; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.wikidata.wdtk.datamodel.interfaces.EntityIdValue; import java.util.HashMap; @@ -38,18 +41,26 @@ import java.util.stream.Collectors; * Runs a collection of edit scrutinizers on an edit batch. * * @author Antonin Delpeuch - * */ public class EditInspector { - private Map scrutinizers; + private static final Logger logger = LoggerFactory.getLogger(EditInspector.class); + + Map scrutinizers; private QAWarningStore warningStore; private ConstraintFetcher fetcher; + private Manifest manifest; - public EditInspector(QAWarningStore warningStore) { + public EditInspector(QAWarningStore warningStore, Manifest manifest) { this.scrutinizers = new HashMap<>(); - this.fetcher = new WikidataConstraintFetcher(EntityCache.getEntityCache()); this.warningStore = warningStore; + this.manifest = manifest; + + String propertyConstraintPid = manifest.getConstraintsRelatedId("property_constraint_pid"); + if (propertyConstraintPid != null) { + EntityCache entityCache = EntityCache.getEntityCache(manifest.getSiteIri(), manifest.getMediaWikiApiEndpoint()); + this.fetcher = new ConstraintFetcher(entityCache, propertyConstraintPid); + } // Register all known scrutinizers here register(new NewItemScrutinizer()); @@ -77,15 +88,23 @@ public class EditInspector { } /** - * Adds a new scrutinizer to the inspector + * Adds a new scrutinizer to the inspector. + * + * If any necessary dependency is missing, the scrutinizer will not be added. * * @param scrutinizer */ public void register(EditScrutinizer scrutinizer) { - String key = scrutinizer.getClass().getName(); - scrutinizers.put(key, scrutinizer); scrutinizer.setStore(warningStore); scrutinizer.setFetcher(fetcher); + scrutinizer.setManifest(manifest); + if (scrutinizer.prepareDependencies()) { + String key = scrutinizer.getClass().getName(); + scrutinizers.put(key, scrutinizer); + } else { + logger.info("scrutinizer [" + scrutinizer.getClass().getSimpleName() + "] is skipped " + + "due to missing of necessary constraint configurations in the Wikibase manifest"); + } } /** diff --git a/extensions/wikidata/src/org/openrefine/wikidata/qa/WikidataConstraintFetcher.java b/extensions/wikidata/src/org/openrefine/wikidata/qa/WikidataConstraintFetcher.java deleted file mode 100644 index 042cb34e1..000000000 --- a/extensions/wikidata/src/org/openrefine/wikidata/qa/WikidataConstraintFetcher.java +++ /dev/null @@ -1,92 +0,0 @@ -/******************************************************************************* - * MIT License - * - * Copyright (c) 2018 Antonin Delpeuch - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * 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 OR COPYRIGHT HOLDERS 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. - ******************************************************************************/ -package org.openrefine.wikidata.qa; - -import org.openrefine.wikidata.utils.EntityCache; -import org.wikidata.wdtk.datamodel.interfaces.EntityIdValue; -import org.wikidata.wdtk.datamodel.interfaces.PropertyDocument; -import org.wikidata.wdtk.datamodel.interfaces.PropertyIdValue; -import org.wikidata.wdtk.datamodel.interfaces.Statement; -import org.wikidata.wdtk.datamodel.interfaces.StatementGroup; -import org.wikidata.wdtk.datamodel.interfaces.StatementRank; - -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -/** - * This class provides an abstraction over the way constraint definitions are - * stored in Wikidata. - * - * @author Antonin Delpeuch - * - */ -public class WikidataConstraintFetcher implements ConstraintFetcher { - - public static String WIKIDATA_CONSTRAINT_PID = "P2302"; - - protected EntityCache entityCache; - - public WikidataConstraintFetcher(EntityCache cache) { - entityCache = cache; - } - - /** - * Gets the list of constraints of a particular type for a property - * - * @param pid - * the property to retrieve the constraints for - * @param qid - * the type of the constraints - * @return the list of matching constraint statements - */ - @Override - public List getConstraintsByType(PropertyIdValue pid, String qid) { - Stream allConstraints = getConstraintStatements(pid).stream() - .filter(s -> s.getValue() != null && ((EntityIdValue) s.getValue()).getId().equals(qid)) - .filter(s -> !StatementRank.DEPRECATED.equals(s.getRank())); - return allConstraints.collect(Collectors.toList()); - } - - /** - * Gets all the constraint statements for a given property - * - * @param pid - * the id of the property to retrieve the constraints for - * @return the list of constraint statements - */ - protected List getConstraintStatements(PropertyIdValue pid) { - PropertyDocument doc = (PropertyDocument) entityCache.get(pid); - StatementGroup group = doc.findStatementGroup(WIKIDATA_CONSTRAINT_PID); - if (group != null) { - return group.getStatements().stream() - .filter(s -> s.getValue() != null && s.getValue() instanceof EntityIdValue) - .collect(Collectors.toList()); - } else { - return new ArrayList(); - } - } - -} diff --git a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/CalendarScrutinizer.java b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/CalendarScrutinizer.java index 05fb1b43e..894a89b71 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/CalendarScrutinizer.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/CalendarScrutinizer.java @@ -28,4 +28,8 @@ public class CalendarScrutinizer extends ValueScrutinizer { } } + @Override + public boolean prepareDependencies() { + return true; + } } diff --git a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/CommonDescriptionScrutinizer.java b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/CommonDescriptionScrutinizer.java index 1ee210f7e..8811eba5a 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/CommonDescriptionScrutinizer.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/CommonDescriptionScrutinizer.java @@ -57,4 +57,8 @@ public class CommonDescriptionScrutinizer extends DescriptionScrutinizer { } } + @Override + public boolean prepareDependencies() { + return true; + } } diff --git a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/ConflictsWithScrutinizer.java b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/ConflictsWithScrutinizer.java index 07e79359b..562a6c555 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/ConflictsWithScrutinizer.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/ConflictsWithScrutinizer.java @@ -18,9 +18,9 @@ import java.util.Set; public class ConflictsWithScrutinizer extends EditScrutinizer { public static final String type = "having-conflicts-with-statements"; - public static String CONFLICTS_WITH_CONSTRAINT_QID = "Q21502838"; - public static String CONFLICTS_WITH_PROPERTY_PID = "P2306"; - public static String ITEM_OF_PROPERTY_CONSTRAINT_PID = "P2305"; + public String conflictsWithConstraintQid; + public String conflictsWithPropertyPid; + public String itemOfPropertyConstraintPid; class ConflictsWithConstraint { final PropertyIdValue conflictingPid; @@ -32,10 +32,10 @@ public class ConflictsWithScrutinizer extends EditScrutinizer { this.itemList = new ArrayList<>(); for(SnakGroup group : specs) { for (Snak snak : group.getSnaks()) { - if (group.getProperty().getId().equals(CONFLICTS_WITH_PROPERTY_PID)){ + if (group.getProperty().getId().equals(conflictsWithPropertyPid)){ pid = (PropertyIdValue) snak.getValue(); } - if (group.getProperty().getId().equals(ITEM_OF_PROPERTY_CONSTRAINT_PID)){ + if (group.getProperty().getId().equals(itemOfPropertyConstraintPid)){ this.itemList.add(snak.getValue()); } } @@ -44,6 +44,16 @@ public class ConflictsWithScrutinizer extends EditScrutinizer { } } + @Override + public boolean prepareDependencies() { + conflictsWithConstraintQid = getConstraintsRelatedId("conflicts_with_constraint_qid"); + conflictsWithPropertyPid = getConstraintsRelatedId("property_pid"); + itemOfPropertyConstraintPid = getConstraintsRelatedId("item_of_property_constraint_pid"); + + return _fetcher != null && conflictsWithConstraintQid != null + && conflictsWithPropertyPid != null && itemOfPropertyConstraintPid != null; + } + @Override public void scrutinize(ItemUpdate update) { Map> propertyIdValueValueMap = new HashMap<>(); @@ -64,7 +74,7 @@ public class ConflictsWithScrutinizer extends EditScrutinizer { } for(PropertyIdValue propertyId : propertyIdValueValueMap.keySet()){ - List statementList = _fetcher.getConstraintsByType(propertyId, CONFLICTS_WITH_CONSTRAINT_QID); + List statementList = _fetcher.getConstraintsByType(propertyId, conflictsWithConstraintQid); for (Statement statement : statementList) { ConflictsWithConstraint constraint = new ConflictsWithConstraint(statement); PropertyIdValue conflictingPid = constraint.conflictingPid; diff --git a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/DifferenceWithinRangeScrutinizer.java b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/DifferenceWithinRangeScrutinizer.java index 065166fc2..e51043317 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/DifferenceWithinRangeScrutinizer.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/DifferenceWithinRangeScrutinizer.java @@ -11,10 +11,10 @@ import java.util.Map; public class DifferenceWithinRangeScrutinizer extends EditScrutinizer { public static final String type = "difference-of-the-properties-is-not-within-the-specified-range"; - public static String DIFFERENCE_WITHIN_RANGE_CONSTRAINT_QID = "Q21510854"; - public static String DIFFERENCE_WITHIN_RANGE_CONSTRAINT_PID = "P2306"; - public static String MINIMUM_VALUE_PID = "P2313"; - public static String MAXIMUM_VALUE_PID = "P2312"; + public String differenceWithinRangeConstraintQid; + public String differenceWithinRangeConstraintPid; + public String minimumValuePid; + public String maximumValuePid; class DifferenceWithinRangeConstraint { PropertyIdValue lowerPropertyIdValue; @@ -23,9 +23,9 @@ public class DifferenceWithinRangeScrutinizer extends EditScrutinizer { DifferenceWithinRangeConstraint(Statement statement) { List specs = statement.getClaim().getQualifiers(); if (specs != null) { - List lowerValueProperty = findValues(specs, DIFFERENCE_WITHIN_RANGE_CONSTRAINT_PID); - List minValue = findValues(specs, MINIMUM_VALUE_PID); - List maxValue = findValues(specs, MAXIMUM_VALUE_PID); + List lowerValueProperty = findValues(specs, differenceWithinRangeConstraintPid); + List minValue = findValues(specs, minimumValuePid); + List maxValue = findValues(specs, maximumValuePid); if (!lowerValueProperty.isEmpty()) { lowerPropertyIdValue = (PropertyIdValue) lowerValueProperty.get(0); } @@ -39,6 +39,16 @@ public class DifferenceWithinRangeScrutinizer extends EditScrutinizer { } } + @Override + public boolean prepareDependencies() { + differenceWithinRangeConstraintQid = getConstraintsRelatedId("difference_within_range_constraint_qid"); + differenceWithinRangeConstraintPid = getConstraintsRelatedId("property_pid"); + minimumValuePid = getConstraintsRelatedId("minimum_value_pid"); + maximumValuePid = getConstraintsRelatedId("maximum_value_pid"); + return _fetcher != null && differenceWithinRangeConstraintQid != null && differenceWithinRangeConstraintPid != null + && minimumValuePid != null && maximumValuePid != null; + } + @Override public void scrutinize(ItemUpdate update) { Map propertyIdValueValueMap = new HashMap<>(); @@ -49,7 +59,7 @@ public class DifferenceWithinRangeScrutinizer extends EditScrutinizer { } for(PropertyIdValue propertyId : propertyIdValueValueMap.keySet()){ - List statementList = _fetcher.getConstraintsByType(propertyId, DIFFERENCE_WITHIN_RANGE_CONSTRAINT_QID); + List statementList = _fetcher.getConstraintsByType(propertyId, differenceWithinRangeConstraintQid); if (!statementList.isEmpty()){ DifferenceWithinRangeConstraint constraint = new DifferenceWithinRangeConstraint(statementList.get(0)); PropertyIdValue lowerPropertyId = constraint.lowerPropertyIdValue; diff --git a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/DistinctValuesScrutinizer.java b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/DistinctValuesScrutinizer.java index 1e0e2c344..c83baac6c 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/DistinctValuesScrutinizer.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/DistinctValuesScrutinizer.java @@ -43,7 +43,7 @@ import java.util.Map; public class DistinctValuesScrutinizer extends StatementScrutinizer { public final static String type = "identical-values-for-distinct-valued-property"; - public static String DISTINCT_VALUES_CONSTRAINT_QID = "Q21502410"; + public String distinctValuesConstraintQid; private Map> _seenValues; @@ -51,10 +51,16 @@ public class DistinctValuesScrutinizer extends StatementScrutinizer { _seenValues = new HashMap<>(); } + @Override + public boolean prepareDependencies() { + distinctValuesConstraintQid = getConstraintsRelatedId("distinct_values_constraint_qid"); + return _fetcher != null && distinctValuesConstraintQid != null; + } + @Override public void scrutinize(Statement statement, EntityIdValue entityId, boolean added) { PropertyIdValue pid = statement.getClaim().getMainSnak().getPropertyId(); - List statementList = _fetcher.getConstraintsByType(pid, DISTINCT_VALUES_CONSTRAINT_QID); + List statementList = _fetcher.getConstraintsByType(pid, distinctValuesConstraintQid); if (!statementList.isEmpty()) { Value mainSnakValue = statement.getClaim().getMainSnak().getValue(); Map seen = _seenValues.get(pid); diff --git a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/EditScrutinizer.java b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/EditScrutinizer.java index fdf3534ce..ebab5e25c 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/EditScrutinizer.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/EditScrutinizer.java @@ -23,6 +23,7 @@ ******************************************************************************/ package org.openrefine.wikidata.qa.scrutinizers; +import org.openrefine.wikidata.manifests.Manifest; import org.openrefine.wikidata.qa.ConstraintFetcher; import org.openrefine.wikidata.qa.QAWarning; import org.openrefine.wikidata.qa.QAWarning.Severity; @@ -44,19 +45,35 @@ public abstract class EditScrutinizer { protected QAWarningStore _store; protected ConstraintFetcher _fetcher; - - public EditScrutinizer() { - _fetcher = null; - _store = null; - } + protected Manifest manifest; public void setStore(QAWarningStore store) { _store = store; } + /** + * The fetcher will be set to null if 'property_constraint_pid' is missing in the manifest. + */ public void setFetcher(ConstraintFetcher fetcher) { _fetcher = fetcher; } + + public void setManifest(Manifest manifest) { + this.manifest = manifest; + } + + public String getConstraintsRelatedId(String name) { + return manifest.getConstraintsRelatedId(name); + } + + /** + * Prepare the dependencies(i.e. constraint-related pids and qids) needed by the scrutinizer. + * + * Called before {@link EditScrutinizer#batchIsBeginning()}. + * + * @return false if any necessary dependency is missing, true otherwise. + */ + public abstract boolean prepareDependencies(); /** * Called before an edit batch is scrutinized. @@ -97,8 +114,6 @@ public abstract class EditScrutinizer { /** * Helper to be used by subclasses to emit simple INFO warnings - * - * @param warning */ protected void info(String type) { addIssue(type, null, QAWarning.Severity.INFO, 1); @@ -107,8 +122,6 @@ public abstract class EditScrutinizer { /** * Helper to be used by subclasses to emit simple warnings - * - * @param warning */ protected void warning(String type) { addIssue(type, null, QAWarning.Severity.WARNING, 1); @@ -116,8 +129,6 @@ public abstract class EditScrutinizer { /** * Helper to be used by subclasses to emit simple important warnings - * - * @param warning */ protected void important(String type) { addIssue(type, null, QAWarning.Severity.IMPORTANT, 1); @@ -125,8 +136,6 @@ public abstract class EditScrutinizer { /** * Helper to be used by subclasses to emit simple critical warnings - * - * @param warning */ protected void critical(String type) { addIssue(type, null, QAWarning.Severity.CRITICAL, 1); diff --git a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/EnglishDescriptionScrutinizer.java b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/EnglishDescriptionScrutinizer.java index ec8a0fa33..d149fca61 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/EnglishDescriptionScrutinizer.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/EnglishDescriptionScrutinizer.java @@ -71,4 +71,8 @@ public class EnglishDescriptionScrutinizer extends DescriptionScrutinizer { } } + @Override + public boolean prepareDependencies() { + return true; + } } diff --git a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/EntityTypeScrutinizer.java b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/EntityTypeScrutinizer.java index fd6090e64..e532d867b 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/EntityTypeScrutinizer.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/EntityTypeScrutinizer.java @@ -14,20 +14,20 @@ import java.util.List; public class EntityTypeScrutinizer extends SnakScrutinizer { public final static String type = "invalid-entity-type"; - public static String ALLOWED_ENTITY_TYPES_QID = "Q52004125"; - public static String ALLOWED_ITEM_TYPE_QID = "Q29934200"; - public static String ALLOWED_ENTITY_TYPES_PID = "P2305"; + public String allowedEntityTypesQid; + public String wikibaseItemQid; + public String itemOfPropertyConstraint; @Override public void scrutinize(Snak snak, EntityIdValue entityId, boolean added) { PropertyIdValue pid = snak.getPropertyId(); - List statementList = _fetcher.getConstraintsByType(pid, ALLOWED_ENTITY_TYPES_QID); + List statementList = _fetcher.getConstraintsByType(pid, allowedEntityTypesQid); if(!statementList.isEmpty()) { List constraint = statementList.get(0).getClaim().getQualifiers(); boolean isUsable = true; if (constraint != null) { - isUsable = findValues(constraint, ALLOWED_ENTITY_TYPES_PID).contains( - Datamodel.makeWikidataItemIdValue(ALLOWED_ITEM_TYPE_QID)); + isUsable = findValues(constraint, itemOfPropertyConstraint).contains( + Datamodel.makeWikidataItemIdValue(wikibaseItemQid)); } if (!isUsable) { QAWarning issue = new QAWarning(type, null, QAWarning.Severity.WARNING, 1); @@ -37,4 +37,13 @@ public class EntityTypeScrutinizer extends SnakScrutinizer { } } } + + @Override + public boolean prepareDependencies() { + allowedEntityTypesQid = getConstraintsRelatedId("allowed_entity_types_constraint_qid"); + wikibaseItemQid = getConstraintsRelatedId("wikibase_item_qid"); + itemOfPropertyConstraint = getConstraintsRelatedId("item_of_property_constraint_pid"); + return _fetcher != null && allowedEntityTypesQid != null + && wikibaseItemQid != null && itemOfPropertyConstraint != null; + } } diff --git a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/FormatScrutinizer.java b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/FormatScrutinizer.java index edd8995a3..2151687fc 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/FormatScrutinizer.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/FormatScrutinizer.java @@ -49,8 +49,8 @@ import java.util.regex.Pattern; public class FormatScrutinizer extends SnakScrutinizer { public static final String type = "add-statements-with-invalid-format"; - public static String FORMAT_CONSTRAINT_QID = "Q21502404"; - public static String FORMAT_REGEX_PID = "P1793"; + public String formatConstraintQid; + public String formatRegexPid; private Map> _patterns; @@ -60,7 +60,7 @@ public class FormatScrutinizer extends SnakScrutinizer { FormatConstraint(Statement statement) { List constraint = statement.getClaim().getQualifiers(); if (constraint != null) { - List regexes = findValues(constraint, FORMAT_REGEX_PID); + List regexes = findValues(constraint, formatRegexPid); if (!regexes.isEmpty()) { regularExpressionFormat = ((StringValue) regexes.get(0)).getString(); } @@ -71,6 +71,13 @@ public class FormatScrutinizer extends SnakScrutinizer { _patterns = new HashMap<>(); } + @Override + public boolean prepareDependencies() { + formatConstraintQid = getConstraintsRelatedId("format_constraint_qid"); + formatRegexPid = getConstraintsRelatedId("format_as_a_regular_expression_pid"); + return _fetcher != null && formatConstraintQid != null && formatRegexPid != null; + } + /** * Loads the regex for a property and compiles it to a pattern (this is cached * upstream, plus we are doing it only once per property and batch). @@ -83,7 +90,7 @@ public class FormatScrutinizer extends SnakScrutinizer { if (_patterns.containsKey(pid)) { return _patterns.get(pid); } else { - List statementList = _fetcher.getConstraintsByType(pid, FORMAT_CONSTRAINT_QID); + List statementList = _fetcher.getConstraintsByType(pid, formatConstraintQid); Set patterns = new HashSet<>(); for (Statement statement: statementList) { FormatConstraint constraint = new FormatConstraint(statement); diff --git a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/InverseConstraintScrutinizer.java b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/InverseConstraintScrutinizer.java index 7e599e7ea..315324a89 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/InverseConstraintScrutinizer.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/InverseConstraintScrutinizer.java @@ -47,9 +47,9 @@ import java.util.Set; public class InverseConstraintScrutinizer extends StatementScrutinizer { public static final String type = "missing-inverse-statements"; - public static String INVERSE_CONSTRAINT_QID = "Q21510855"; - public static String INVERSE_PROPERTY_PID = "P2306"; - public static String SYMMETRIC_CONSTRAINT_QID = "Q21510862"; + public String inverseConstraintQid; + public String inversePropertyPid; + public String symmetricConstraintQid; class InverseConstraint { PropertyIdValue propertyParameterValue; @@ -58,7 +58,7 @@ public class InverseConstraintScrutinizer extends StatementScrutinizer { List specs = statement.getClaim().getQualifiers(); if (specs != null) { - List inverses = findValues(specs, INVERSE_PROPERTY_PID); + List inverses = findValues(specs, inversePropertyPid); if (!inverses.isEmpty()) { propertyParameterValue = (PropertyIdValue) inverses.get(0); } @@ -74,17 +74,26 @@ public class InverseConstraintScrutinizer extends StatementScrutinizer { _statements = new HashMap<>(); } + @Override + public boolean prepareDependencies() { + inverseConstraintQid = getConstraintsRelatedId("inverse_constraint_qid"); + inversePropertyPid = getConstraintsRelatedId("property_pid"); + symmetricConstraintQid = getConstraintsRelatedId("symmetric_constraint_qid"); + return _fetcher != null && inverseConstraintQid != null + && inversePropertyPid != null && symmetricConstraintQid != null; + } + protected PropertyIdValue getInverseConstraint(PropertyIdValue pid) { if (_inverse.containsKey(pid)) { return _inverse.get(pid); } else { PropertyIdValue inversePid = null; - List statementList = _fetcher.getConstraintsByType(pid, INVERSE_CONSTRAINT_QID); + List statementList = _fetcher.getConstraintsByType(pid, inverseConstraintQid); if (!statementList.isEmpty()) { InverseConstraint constraint = new InverseConstraint(statementList.get(0)); inversePid = constraint.propertyParameterValue; } - if (inversePid == null && !_fetcher.getConstraintsByType(pid, SYMMETRIC_CONSTRAINT_QID).isEmpty()) { + if (inversePid == null && !_fetcher.getConstraintsByType(pid, symmetricConstraintQid).isEmpty()) { inversePid = pid; } _inverse.put(pid, inversePid); diff --git a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/ItemRequiresScrutinizer.java b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/ItemRequiresScrutinizer.java index 391c21d5e..9598ba19e 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/ItemRequiresScrutinizer.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/ItemRequiresScrutinizer.java @@ -21,9 +21,9 @@ public class ItemRequiresScrutinizer extends EditScrutinizer { public static final String newItemRequirePropertyType = "new-item-requires-certain-other-statement"; public static final String existingItemRequireValuesType = "existing-item-requires-property-to-have-certain-values"; public static final String existingItemRequirePropertyType = "existing-item-requires-certain-other-statement"; - public static String ITEM_REQUIRES_CONSTRAINT_QID = "Q21503247"; - public static String ITEM_REQUIRES_PROPERTY_PID = "P2306"; - public static String ITEM_OF_PROPERTY_CONSTRAINT_PID = "P2305"; + public String itemRequiresConstraintQid; + public String itemRequiresPropertyPid; + public String itemOfPropertyConstraintPid; class ItemRequiresConstraint { final PropertyIdValue itemRequiresPid; @@ -35,10 +35,10 @@ public class ItemRequiresScrutinizer extends EditScrutinizer { this.itemList = new ArrayList<>(); for(SnakGroup group : specs) { for (Snak snak : group.getSnaks()) { - if (group.getProperty().getId().equals(ITEM_REQUIRES_PROPERTY_PID)){ + if (group.getProperty().getId().equals(itemRequiresPropertyPid)){ pid = (PropertyIdValue) snak.getValue(); } - if (group.getProperty().getId().equals(ITEM_OF_PROPERTY_CONSTRAINT_PID)){ + if (group.getProperty().getId().equals(itemOfPropertyConstraintPid)){ this.itemList.add(snak.getValue()); } } @@ -47,6 +47,15 @@ public class ItemRequiresScrutinizer extends EditScrutinizer { } } + @Override + public boolean prepareDependencies() { + itemRequiresConstraintQid = getConstraintsRelatedId("item_requires_statement_constraint_qid"); + itemRequiresPropertyPid = getConstraintsRelatedId("property_pid"); + itemOfPropertyConstraintPid = getConstraintsRelatedId("item_of_property_constraint_pid"); + return _fetcher != null && itemRequiresConstraintQid != null + && itemRequiresPropertyPid != null && itemOfPropertyConstraintPid != null; + } + @Override public void scrutinize(ItemUpdate update) { Map> propertyIdValueValueMap = new HashMap<>(); @@ -66,7 +75,7 @@ public class ItemRequiresScrutinizer extends EditScrutinizer { } for (PropertyIdValue propertyId : propertyIdValueValueMap.keySet()) { - List constraintDefinitions = _fetcher.getConstraintsByType(propertyId, ITEM_REQUIRES_CONSTRAINT_QID); + List constraintDefinitions = _fetcher.getConstraintsByType(propertyId, itemRequiresConstraintQid); for (Statement statement : constraintDefinitions) { ItemRequiresConstraint constraint = new ItemRequiresConstraint(statement); PropertyIdValue itemRequiresPid = constraint.itemRequiresPid; diff --git a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/MultiValueScrutinizer.java b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/MultiValueScrutinizer.java index 2cc512c54..81b36352e 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/MultiValueScrutinizer.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/MultiValueScrutinizer.java @@ -3,10 +3,7 @@ package org.openrefine.wikidata.qa.scrutinizers; import org.openrefine.wikidata.qa.QAWarning; import org.openrefine.wikidata.updates.ItemUpdate; import org.wikidata.wdtk.datamodel.interfaces.PropertyIdValue; -import org.wikidata.wdtk.datamodel.interfaces.Snak; -import org.wikidata.wdtk.datamodel.interfaces.SnakGroup; import org.wikidata.wdtk.datamodel.interfaces.Statement; -import org.wikidata.wdtk.datamodel.interfaces.Value; import java.util.HashMap; import java.util.List; @@ -16,7 +13,13 @@ public class MultiValueScrutinizer extends EditScrutinizer { public static final String new_type = "multi-valued-property-is-required-for-new-item"; public static final String existing_type = "multi-valued-property-is-required-for-existing-item"; - public static String MULTI_VALUE_CONSTRAINT_QID = "Q21510857"; + public String multiValueConstraintQid; + + @Override + public boolean prepareDependencies() { + multiValueConstraintQid = getConstraintsRelatedId("multi_value_constraint_qid"); + return _fetcher != null && multiValueConstraintQid != null; + } @Override public void scrutinize(ItemUpdate update) { @@ -24,7 +27,7 @@ public class MultiValueScrutinizer extends EditScrutinizer { for (Statement statement : update.getAddedStatements()) { PropertyIdValue pid = statement.getClaim().getMainSnak().getPropertyId(); - List statementList = _fetcher.getConstraintsByType(pid, MULTI_VALUE_CONSTRAINT_QID); + List statementList = _fetcher.getConstraintsByType(pid, multiValueConstraintQid); if (propertyCount.containsKey(pid)) { propertyCount.put(pid, propertyCount.get(pid) + 1); } else if (!statementList.isEmpty()) { diff --git a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/NewItemScrutinizer.java b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/NewItemScrutinizer.java index 9110b0031..54fc81b31 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/NewItemScrutinizer.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/NewItemScrutinizer.java @@ -37,9 +37,14 @@ public class NewItemScrutinizer extends EditScrutinizer { public static final String noLabelType = "new-item-without-labels-or-aliases"; public static final String noDescType = "new-item-without-descriptions"; public static final String deletedStatementsType = "new-item-with-deleted-statements"; - public static final String noTypeType = "new-item-without-P31-or-P279"; + public static final String noTypeType = "new-item-without-instance-of-or-subclass-of"; public static final String newItemType = "new-item-created"; + @Override + public boolean prepareDependencies() { + return true; + } + @Override public void scrutinize(ItemUpdate update) { if (update.isNew()) { @@ -67,7 +72,7 @@ public class NewItemScrutinizer extends EditScrutinizer { boolean typeFound = false; for (StatementGroup group : update.getAddedStatementGroups()) { String pid = group.getProperty().getId(); - if ("P31".equals(pid) || "P279".equals(pid)) { + if (manifest.getInstanceOfPid().equals(pid) || manifest.getSubclassOfPid().equals(pid)) { typeFound = true; break; } diff --git a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/NoEditsMadeScrutinizer.java b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/NoEditsMadeScrutinizer.java index 27e653b03..a9d567f78 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/NoEditsMadeScrutinizer.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/NoEditsMadeScrutinizer.java @@ -30,7 +30,12 @@ public class NoEditsMadeScrutinizer extends EditScrutinizer { public static final String type = "no-edit-generated"; private boolean nonNullUpdateSeen = false; - + + @Override + public boolean prepareDependencies() { + return true; + } + @Override public void batchIsBeginning() { nonNullUpdateSeen = false; diff --git a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/QualifierCompatibilityScrutinizer.java b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/QualifierCompatibilityScrutinizer.java index 330acb9bd..5eb9c36fe 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/QualifierCompatibilityScrutinizer.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/QualifierCompatibilityScrutinizer.java @@ -47,11 +47,12 @@ public class QualifierCompatibilityScrutinizer extends StatementScrutinizer { public static final String missingMandatoryQualifiersType = "missing-mandatory-qualifiers"; public static final String disallowedQualifiersType = "disallowed-qualifiers"; - public static String ALLOWED_QUALIFIERS_CONSTRAINT_QID = "Q21510851"; - public static String ALLOWED_QUALIFIERS_CONSTRAINT_PID = "P2306"; - public static String MANDATORY_QUALIFIERS_CONSTRAINT_QID = "Q21510856"; - public static String MANDATORY_QUALIFIERS_CONSTRAINT_PID = "P2306"; + public String allowedQualifiersConstraintQid; + public String allowedQualifiersConstraintPid; + + public String mandatoryQualifiersConstraintQid; + public String mandatoryQualifiersConstraintPid; class AllowedQualifierConstraint { Set allowedProperties; @@ -59,7 +60,7 @@ public class QualifierCompatibilityScrutinizer extends StatementScrutinizer { allowedProperties = new HashSet<>(); List specs = statement.getClaim().getQualifiers(); if (specs != null) { - List properties = findValues(specs, ALLOWED_QUALIFIERS_CONSTRAINT_PID); + List properties = findValues(specs, allowedQualifiersConstraintPid); allowedProperties = properties.stream() .filter(e -> e != null) .map(e -> (PropertyIdValue) e) @@ -75,7 +76,7 @@ public class QualifierCompatibilityScrutinizer extends StatementScrutinizer { mandatoryProperties = new HashSet<>(); List specs = statement.getClaim().getQualifiers(); if (specs != null) { - List properties = findValues(specs, MANDATORY_QUALIFIERS_CONSTRAINT_PID); + List properties = findValues(specs, mandatoryQualifiersConstraintPid); mandatoryProperties = properties.stream() .filter(e -> e != null) .map(e -> (PropertyIdValue) e) @@ -92,12 +93,22 @@ public class QualifierCompatibilityScrutinizer extends StatementScrutinizer { _mandatoryQualifiers = new HashMap<>(); } + @Override + public boolean prepareDependencies() { + allowedQualifiersConstraintQid = getConstraintsRelatedId("allowed_qualifiers_constraint_qid"); + allowedQualifiersConstraintPid = getConstraintsRelatedId("property_pid"); + mandatoryQualifiersConstraintQid = getConstraintsRelatedId("mandatory_qualifier_constraint_qid"); + mandatoryQualifiersConstraintPid = getConstraintsRelatedId("property_pid"); + return _fetcher != null && allowedQualifiersConstraintQid != null && allowedQualifiersConstraintPid != null && + mandatoryQualifiersConstraintQid != null && mandatoryQualifiersConstraintPid != null; + } + protected boolean qualifierIsAllowed(PropertyIdValue statementProperty, PropertyIdValue qualifierProperty) { Set allowed = null; if (_allowedQualifiers.containsKey(statementProperty)) { allowed = _allowedQualifiers.get(statementProperty); } else { - List statementList = _fetcher.getConstraintsByType(statementProperty, ALLOWED_QUALIFIERS_CONSTRAINT_QID); + List statementList = _fetcher.getConstraintsByType(statementProperty, allowedQualifiersConstraintQid); if (!statementList.isEmpty()){ AllowedQualifierConstraint allowedQualifierConstraint = new AllowedQualifierConstraint(statementList.get(0)); allowed = allowedQualifierConstraint.allowedProperties; @@ -112,7 +123,7 @@ public class QualifierCompatibilityScrutinizer extends StatementScrutinizer { if (_mandatoryQualifiers.containsKey(statementProperty)) { mandatory = _mandatoryQualifiers.get(statementProperty); } else { - List statementList = _fetcher.getConstraintsByType(statementProperty, MANDATORY_QUALIFIERS_CONSTRAINT_QID); + List statementList = _fetcher.getConstraintsByType(statementProperty, mandatoryQualifiersConstraintQid); if (!statementList.isEmpty()){ MandatoryQualifierConstraint mandatoryQualifierConstraint = new MandatoryQualifierConstraint(statementList.get(0)); mandatory = mandatoryQualifierConstraint.mandatoryProperties; diff --git a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/QuantityScrutinizer.java b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/QuantityScrutinizer.java index 816d48d9f..8073b1881 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/QuantityScrutinizer.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/QuantityScrutinizer.java @@ -27,18 +27,28 @@ public class QuantityScrutinizer extends SnakScrutinizer { public static final String invalidUnitType = "invalid-unit"; public static final String noUnitProvidedType = "no-unit-provided"; - public static String NO_BOUNDS_CONSTRAINT_QID = "Q51723761"; - public static String INTEGER_VALUED_CONSTRAINT_QID = "Q52848401"; + public String noBoundsConstraintQid; + public String integerValuedConstraintQid; - public static String ALLOWED_UNITS_CONSTRAINT_QID = "Q21514353"; - public static String ALLOWED_UNITS_CONSTRAINT_PID = "P2305"; + public String allowedUnitsConstraintQid; + public String allowedUnitsConstraintPid; + + @Override + public boolean prepareDependencies() { + noBoundsConstraintQid = getConstraintsRelatedId("no_bounds_constraint_qid"); + integerValuedConstraintQid = getConstraintsRelatedId("integer_constraint_qid"); + allowedUnitsConstraintQid = getConstraintsRelatedId("allowed_units_constraint_qid"); + allowedUnitsConstraintPid = getConstraintsRelatedId("item_of_property_constraint_pid"); + return _fetcher != null && noBoundsConstraintQid != null && integerValuedConstraintQid != null + && allowedUnitsConstraintQid != null && allowedUnitsConstraintPid != null; + } class AllowedUnitsConstraint { Set allowedUnits; AllowedUnitsConstraint(Statement statement) { List specs = statement.getClaim().getQualifiers(); if (specs != null) { - List properties = findValues(specs, ALLOWED_UNITS_CONSTRAINT_PID); + List properties = findValues(specs, allowedUnitsConstraintPid); allowedUnits = properties.stream() .map(e -> e == null ? null : (ItemIdValue) e) .collect(Collectors.toSet()); @@ -52,21 +62,21 @@ public class QuantityScrutinizer extends SnakScrutinizer { PropertyIdValue pid = snak.getPropertyId(); QuantityValue value = (QuantityValue)snak.getValue(); - if(!_fetcher.getConstraintsByType(pid, NO_BOUNDS_CONSTRAINT_QID).isEmpty() && (value.getUpperBound() != null || value.getLowerBound() != null)) { + if(!_fetcher.getConstraintsByType(pid, noBoundsConstraintQid).isEmpty() && (value.getUpperBound() != null || value.getLowerBound() != null)) { QAWarning issue = new QAWarning(boundsDisallowedType, pid.getId(), QAWarning.Severity.IMPORTANT, 1); issue.setProperty("property_entity", pid); issue.setProperty("example_value", value.getNumericValue().toString()); issue.setProperty("example_item_entity", entityId); addIssue(issue); } - if(!_fetcher.getConstraintsByType(pid, INTEGER_VALUED_CONSTRAINT_QID).isEmpty() && value.getNumericValue().scale() > 0) { + if(!_fetcher.getConstraintsByType(pid, integerValuedConstraintQid).isEmpty() && value.getNumericValue().scale() > 0) { QAWarning issue = new QAWarning(integerConstraintType, pid.getId(), QAWarning.Severity.IMPORTANT, 1); issue.setProperty("property_entity", pid); issue.setProperty("example_value", value.getNumericValue().toString()); issue.setProperty("example_item_entity", entityId); addIssue(issue); } - List statementList = _fetcher.getConstraintsByType(pid, ALLOWED_UNITS_CONSTRAINT_QID); + List statementList = _fetcher.getConstraintsByType(pid, allowedUnitsConstraintQid); Set allowedUnits = null; if (!statementList.isEmpty()) { AllowedUnitsConstraint allowedUnitsConstraint = new AllowedUnitsConstraint(statementList.get(0)); diff --git a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/RestrictedPositionScrutinizer.java b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/RestrictedPositionScrutinizer.java index ea76c53e9..3e14e8ac3 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/RestrictedPositionScrutinizer.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/RestrictedPositionScrutinizer.java @@ -39,11 +39,22 @@ import java.util.List; public class RestrictedPositionScrutinizer extends StatementScrutinizer { - public static String SCOPE_CONSTRAINT_QID = "Q53869507"; - public static String SCOPE_CONSTRAINT_PID = "P5314"; - public static String SCOPE_CONSTRAINT_VALUE_QID = "Q54828448"; - public static String SCOPE_CONSTRAINT_QUALIFIER_QID = "Q54828449"; - public static String SCOPE_CONSTRAINT_REFERENCE_QID = "Q54828450"; + public String scopeConstraintQid; + public String scopeConstraintPid; + public String scopeConstraintValueQid; + public String scopeConstraintQualifierQid; + public String scopeConstraintReferenceQid; + + @Override + public boolean prepareDependencies() { + scopeConstraintQid = getConstraintsRelatedId("property_scope_constraint_qid"); + scopeConstraintPid = getConstraintsRelatedId("property_scope_pid"); + scopeConstraintValueQid = getConstraintsRelatedId("as_main_value_qid"); + scopeConstraintQualifierQid = getConstraintsRelatedId("as_qualifiers_qid"); + scopeConstraintReferenceQid = getConstraintsRelatedId("as_references_qid"); + return _fetcher != null && scopeConstraintQid != null && scopeConstraintPid != null && scopeConstraintValueQid != null + && scopeConstraintQualifierQid != null && scopeConstraintReferenceQid != null; + } protected enum SnakPosition { MAINSNAK, QUALIFIER, REFERENCE @@ -54,10 +65,10 @@ public class RestrictedPositionScrutinizer extends StatementScrutinizer { RestrictedPositionConstraint(Statement statement) { List specs = statement.getClaim().getQualifiers(); if (specs != null) { - ItemIdValue targetValue = Datamodel.makeWikidataItemIdValue(SCOPE_CONSTRAINT_VALUE_QID); - ItemIdValue targetQualifier = Datamodel.makeWikidataItemIdValue(SCOPE_CONSTRAINT_QUALIFIER_QID); - ItemIdValue targetReference = Datamodel.makeWikidataItemIdValue(SCOPE_CONSTRAINT_REFERENCE_QID); - List snakValues = findValues(specs, SCOPE_CONSTRAINT_PID); + ItemIdValue targetValue = Datamodel.makeWikidataItemIdValue(scopeConstraintValueQid); + ItemIdValue targetQualifier = Datamodel.makeWikidataItemIdValue(scopeConstraintQualifierQid); + ItemIdValue targetReference = Datamodel.makeWikidataItemIdValue(scopeConstraintReferenceQid); + List snakValues = findValues(specs, scopeConstraintPid); isAllowedAsValue = snakValues.contains(targetValue); isAllowedAsQualifier = snakValues.contains(targetQualifier); isAllowedAsReference = snakValues.contains(targetReference); @@ -99,7 +110,7 @@ public class RestrictedPositionScrutinizer extends StatementScrutinizer { } public boolean positionAllowed(PropertyIdValue pid, SnakPosition position) { - List constraintDefinitions = _fetcher.getConstraintsByType(pid, SCOPE_CONSTRAINT_QID); + List constraintDefinitions = _fetcher.getConstraintsByType(pid, scopeConstraintQid); if (!constraintDefinitions.isEmpty()) { RestrictedPositionConstraint constraint = new RestrictedPositionConstraint(constraintDefinitions.get(0)); if (position.equals(SnakPosition.MAINSNAK)) { diff --git a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/RestrictedValuesScrutinizer.java b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/RestrictedValuesScrutinizer.java index a0c346444..6a2dca6de 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/RestrictedValuesScrutinizer.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/RestrictedValuesScrutinizer.java @@ -15,19 +15,29 @@ import java.util.stream.Collectors; public class RestrictedValuesScrutinizer extends SnakScrutinizer { - public static String type = "forbidden-value"; - public static String ALLOWED_VALUES_CONSTRAINT_QID = "Q21510859"; - public static String ALLOWED_VALUES_CONSTRAINT_PID = "P2305"; + public static final String type = "forbidden-value"; + public String allowedValuesConstraintQid; + public String allowedValuesConstraintPid; - public static String DISALLOWED_VALUES_CONSTRAINT_QID = "Q52558054"; - public static String DISALLOWED_VALUES_CONSTRAINT_PID = "P2305"; + public String disallowedValuesConstraintQid; + public String disallowedValuesConstraintPid; + + @Override + public boolean prepareDependencies() { + allowedValuesConstraintQid = getConstraintsRelatedId("one_of_constraint_qid"); + allowedValuesConstraintPid = getConstraintsRelatedId("item_of_property_constraint_pid"); + disallowedValuesConstraintQid = getConstraintsRelatedId("none_of_constraint_qid"); + disallowedValuesConstraintPid = getConstraintsRelatedId("item_of_property_constraint_pid"); + return _fetcher != null && allowedValuesConstraintQid != null && allowedValuesConstraintPid != null + && disallowedValuesConstraintQid != null && disallowedValuesConstraintPid != null; + } class AllowedValueConstraint { Set allowedValues; AllowedValueConstraint(Statement statement) { List specs = statement.getClaim().getQualifiers(); if (specs != null) { - List properties = findValues(specs, ALLOWED_VALUES_CONSTRAINT_PID); + List properties = findValues(specs, allowedValuesConstraintPid); allowedValues = properties.stream().collect(Collectors.toSet()); } } @@ -38,7 +48,7 @@ public class RestrictedValuesScrutinizer extends SnakScrutinizer { DisallowedValueConstraint(Statement statement) { List specs = statement.getClaim().getQualifiers(); if (specs != null) { - List properties = findValues(specs, DISALLOWED_VALUES_CONSTRAINT_PID); + List properties = findValues(specs, disallowedValuesConstraintPid); disallowedValues = properties.stream().collect(Collectors.toSet()); } } @@ -48,8 +58,8 @@ public class RestrictedValuesScrutinizer extends SnakScrutinizer { public void scrutinize(Snak snak, EntityIdValue entityId, boolean added) { PropertyIdValue pid = snak.getPropertyId(); Value value = snak.getValue(); - List allowedValueConstraintDefinitions = _fetcher.getConstraintsByType(pid, ALLOWED_VALUES_CONSTRAINT_QID); - List disallowedValueConstraintDefinitions = _fetcher.getConstraintsByType(pid, DISALLOWED_VALUES_CONSTRAINT_QID); + List allowedValueConstraintDefinitions = _fetcher.getConstraintsByType(pid, allowedValuesConstraintQid); + List disallowedValueConstraintDefinitions = _fetcher.getConstraintsByType(pid, disallowedValuesConstraintQid); Set allowedValues = null, disallowedValues = null; if (!allowedValueConstraintDefinitions.isEmpty()) { AllowedValueConstraint constraint = new AllowedValueConstraint(allowedValueConstraintDefinitions.get(0)); diff --git a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/SelfReferentialScrutinizer.java b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/SelfReferentialScrutinizer.java index c23f0606e..b16a5014b 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/SelfReferentialScrutinizer.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/SelfReferentialScrutinizer.java @@ -47,4 +47,8 @@ public class SelfReferentialScrutinizer extends SnakScrutinizer { } } + @Override + public boolean prepareDependencies() { + return true; + } } diff --git a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/SingleValueScrutinizer.java b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/SingleValueScrutinizer.java index 44525b678..8bb080b87 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/SingleValueScrutinizer.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/SingleValueScrutinizer.java @@ -45,8 +45,15 @@ import java.util.Set; public class SingleValueScrutinizer extends EditScrutinizer { public static final String type = "single-valued-property-added-more-than-once"; - public static String SINGLE_VALUE_CONSTRAINT_QID = "Q19474404"; - public static String SINGLE_BEST_VALUE_CONSTRAINT_QID = "Q52060874"; + public String singleValueConstraintQid; + public String singleBestValueConstraintQid; + + @Override + public boolean prepareDependencies() { + singleValueConstraintQid = getConstraintsRelatedId("single_value_constraint_qid"); + singleBestValueConstraintQid = getConstraintsRelatedId("single_best_value_constraint_qid"); + return _fetcher != null && singleValueConstraintQid != null && singleBestValueConstraintQid != null; + } @Override public void scrutinize(ItemUpdate update) { @@ -54,8 +61,8 @@ public class SingleValueScrutinizer extends EditScrutinizer { for (Statement statement : update.getAddedStatements()) { PropertyIdValue pid = statement.getClaim().getMainSnak().getPropertyId(); - List constraintStatementList1 = _fetcher.getConstraintsByType(pid, SINGLE_VALUE_CONSTRAINT_QID); - List constraintStatementList2 = _fetcher.getConstraintsByType(pid, SINGLE_BEST_VALUE_CONSTRAINT_QID); + List constraintStatementList1 = _fetcher.getConstraintsByType(pid, singleValueConstraintQid); + List constraintStatementList2 = _fetcher.getConstraintsByType(pid, singleBestValueConstraintQid); if (seenSingleProperties.contains(pid)) { QAWarning issue = new QAWarning(type, pid.getId(), QAWarning.Severity.WARNING, 1); issue.setProperty("property_entity", pid); diff --git a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/UnsourcedScrutinizer.java b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/UnsourcedScrutinizer.java index 464c3f2b4..2e595cfc9 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/UnsourcedScrutinizer.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/UnsourcedScrutinizer.java @@ -39,7 +39,7 @@ import java.util.List; */ public class UnsourcedScrutinizer extends EditScrutinizer { - public static final String CITATION_NEEDED_QID = "Q54554025"; + private String citationNeededConstraintQid; public static final String generalType = "unsourced-statements"; public static final String constraintItemType = "no-references-provided"; @@ -47,7 +47,7 @@ public class UnsourcedScrutinizer extends EditScrutinizer { public void scrutinize(ItemUpdate update) { for (Statement statement : update.getAddedStatements()) { PropertyIdValue pid = statement.getClaim().getMainSnak().getPropertyId(); - List constraintDefinitions = _fetcher.getConstraintsByType(pid, CITATION_NEEDED_QID); + List constraintDefinitions = _fetcher.getConstraintsByType(pid, citationNeededConstraintQid); List referenceList = statement.getReferences(); if (referenceList.isEmpty()) { @@ -63,4 +63,9 @@ public class UnsourcedScrutinizer extends EditScrutinizer { } } + @Override + public boolean prepareDependencies() { + citationNeededConstraintQid = getConstraintsRelatedId("citation_needed_constraint_qid"); + return _fetcher != null && citationNeededConstraintQid != null; + } } diff --git a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/UseAsQualifierScrutinizer.java b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/UseAsQualifierScrutinizer.java index 5fd5f9c9e..f921269fe 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/UseAsQualifierScrutinizer.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/UseAsQualifierScrutinizer.java @@ -17,9 +17,9 @@ import java.util.Map; public class UseAsQualifierScrutinizer extends EditScrutinizer { public static final String type = "values-should-not-be-used-as-qualifier"; - public static String ONE_OF_QUALIFIER_VALUE_PROPERTY_QID = "Q52712340"; - public static String QUALIFIER_PROPERTY_PID = "P2306"; - public static String ITEM_OF_PROPERTY_CONSTRAINT_PID = "P2305"; + public String oneOfQualifierValuePropertyQid; + public String property; + public String itemOfPropertyConstraintPid; class UseAsQualifierConstraint { final PropertyIdValue allowedQualifierPid; @@ -30,10 +30,10 @@ public class UseAsQualifierScrutinizer extends EditScrutinizer { this.itemList = new ArrayList<>(); for(SnakGroup group : specs) { for (Snak snak : group.getSnaks()) { - if (group.getProperty().getId().equals(QUALIFIER_PROPERTY_PID)){ + if (group.getProperty().getId().equals(property)){ pid = (PropertyIdValue) snak.getValue(); } - if (group.getProperty().getId().equals(ITEM_OF_PROPERTY_CONSTRAINT_PID)){ + if (group.getProperty().getId().equals(itemOfPropertyConstraintPid)){ this.itemList.add(snak.getValue()); } } @@ -42,6 +42,14 @@ public class UseAsQualifierScrutinizer extends EditScrutinizer { } } + @Override + public boolean prepareDependencies() { + oneOfQualifierValuePropertyQid = getConstraintsRelatedId("one_of_qualifier_value_property_constraint_qid"); + property = getConstraintsRelatedId("property_pid"); + itemOfPropertyConstraintPid = getConstraintsRelatedId("item_of_property_constraint_pid"); + return _fetcher != null && oneOfQualifierValuePropertyQid != null && property != null && itemOfPropertyConstraintPid != null; + } + @Override public void scrutinize(ItemUpdate update) { for (Statement statement : update.getAddedStatements()) { @@ -63,7 +71,7 @@ public class UseAsQualifierScrutinizer extends EditScrutinizer { } } - List constraintDefinitions = _fetcher.getConstraintsByType(pid, ONE_OF_QUALIFIER_VALUE_PROPERTY_QID); + List constraintDefinitions = _fetcher.getConstraintsByType(pid, oneOfQualifierValuePropertyQid); for (Statement constraintStatement : constraintDefinitions) { UseAsQualifierConstraint constraint = new UseAsQualifierConstraint(constraintStatement); if (qualifiersMap.containsKey(constraint.allowedQualifierPid)) { diff --git a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/WhitespaceScrutinizer.java b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/WhitespaceScrutinizer.java index 165238c4a..b9b6deb48 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/WhitespaceScrutinizer.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/qa/scrutinizers/WhitespaceScrutinizer.java @@ -54,6 +54,11 @@ public class WhitespaceScrutinizer extends ValueScrutinizer { _issuesMap.put(nonPrintableCharsType, Pattern.compile("[\\x00\\x03\\x08\\x0B\\x0C\\x0E-\\x1F]")); } + @Override + public boolean prepareDependencies() { + return true; + } + @Override public void scrutinize(Value value) { String str = null; diff --git a/extensions/wikidata/src/org/openrefine/wikidata/schema/ExpressionContext.java b/extensions/wikidata/src/org/openrefine/wikidata/schema/ExpressionContext.java index fd2efc7c5..126be9866 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/schema/ExpressionContext.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/schema/ExpressionContext.java @@ -42,6 +42,7 @@ import com.google.refine.model.Row; public class ExpressionContext { private String baseIRI; + private String mediaWikiApiEndpoint; private int rowId; private Row row; private ColumnModel columnModel; @@ -52,6 +53,8 @@ public class ExpressionContext { * * @param baseIRI * the siteIRI of the schema + * @param mediaWikiApiEndpoint + * the MediaWiki API endpoint of the Wikibase * @param rowId * the id of the row currently visited * @param row @@ -62,9 +65,10 @@ public class ExpressionContext { * where to store the issues encountered when evaluating (can be set * to null if these issues should be ignored) */ - public ExpressionContext(String baseIRI, int rowId, Row row, ColumnModel columnModel, QAWarningStore warningStore) { + public ExpressionContext(String baseIRI, String mediaWikiApiEndpoint, int rowId, Row row, ColumnModel columnModel, QAWarningStore warningStore) { Validate.notNull(baseIRI); this.baseIRI = baseIRI; + this.mediaWikiApiEndpoint = mediaWikiApiEndpoint; this.rowId = rowId; Validate.notNull(row); this.row = row; @@ -77,6 +81,10 @@ public class ExpressionContext { return baseIRI; } + public String getMediaWikiApiEndpoint() { + return mediaWikiApiEndpoint; + } + /** * Retrieves a cell in the current row, by column name. If the column does not * exist, null is returned. diff --git a/extensions/wikidata/src/org/openrefine/wikidata/schema/WbItemVariable.java b/extensions/wikidata/src/org/openrefine/wikidata/schema/WbItemVariable.java index 84289cc98..ab52b37f5 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/schema/WbItemVariable.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/schema/WbItemVariable.java @@ -62,7 +62,7 @@ public class WbItemVariable extends WbVariableExpr { throws SkipSchemaExpressionException { if (cell.recon != null && (Judgment.Matched.equals(cell.recon.judgment) || Judgment.New.equals(cell.recon.judgment))) { - if (cell.recon.identifierSpace == null || !cell.recon.identifierSpace.equals(Datamodel.SITE_WIKIDATA)) { + if (cell.recon.identifierSpace == null || !cell.recon.identifierSpace.equals(ctxt.getBaseIRI())) { QAWarning warning = new QAWarning("invalid-identifier-space", null, QAWarning.Severity.INFO, 1); warning.setProperty("example_cell", cell.value.toString()); ctxt.addWarning(warning); diff --git a/extensions/wikidata/src/org/openrefine/wikidata/schema/WbLanguageConstant.java b/extensions/wikidata/src/org/openrefine/wikidata/schema/WbLanguageConstant.java index 9ce06c629..4ae17f50a 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/schema/WbLanguageConstant.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/schema/WbLanguageConstant.java @@ -50,17 +50,23 @@ public class WbLanguageConstant implements WbExpression { _langLabel = langLabel; } + public static String normalizeLanguageCode(String lang) { + return normalizeLanguageCode(lang, null); + } + /** * Checks that a language code is valid and returns its preferred version * (converting deprecated language codes to their better values). * * @param lang * a Wikimedia language code + * @param mediaWikiApiEndpoint + * the MediaWiki API endpoint of the Wikibase * @return the normalized code, or null if the code is invalid. */ - public static String normalizeLanguageCode(String lang) { + public static String normalizeLanguageCode(String lang, String mediaWikiApiEndpoint) { try { - if (LanguageCodeStore.ALLOWED_LANGUAGE_CODES.contains(lang)) { + if (LanguageCodeStore.getLanguageCodes(mediaWikiApiEndpoint).contains(lang)) { return WikimediaLanguageCodes.fixLanguageCodeIfDeprecated(lang); } else { return null; diff --git a/extensions/wikidata/src/org/openrefine/wikidata/schema/WbLanguageVariable.java b/extensions/wikidata/src/org/openrefine/wikidata/schema/WbLanguageVariable.java index 0e2e37914..6b8e36ba4 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/schema/WbLanguageVariable.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/schema/WbLanguageVariable.java @@ -56,7 +56,8 @@ public class WbLanguageVariable extends WbVariableExpr { throws SkipSchemaExpressionException { if (cell.value != null && !cell.value.toString().isEmpty()) { String code = cell.value.toString().trim(); - String normalized = WbLanguageConstant.normalizeLanguageCode(code); + String mediaWikiApiEndpoint = ctxt.getMediaWikiApiEndpoint(); + String normalized = WbLanguageConstant.normalizeLanguageCode(code, mediaWikiApiEndpoint); if (normalized != null) { return normalized; } else { diff --git a/extensions/wikidata/src/org/openrefine/wikidata/schema/WikibaseSchema.java b/extensions/wikidata/src/org/openrefine/wikidata/schema/WikibaseSchema.java index 7230cbef9..82f0bc734 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/schema/WikibaseSchema.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/schema/WikibaseSchema.java @@ -28,6 +28,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import com.fasterxml.jackson.annotation.JsonIgnore; import org.openrefine.wikidata.qa.QAWarningStore; import org.openrefine.wikidata.schema.exceptions.SkipSchemaExpressionException; import org.openrefine.wikidata.updates.ItemUpdate; @@ -35,7 +36,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.refine.browsing.Engine; @@ -59,9 +59,16 @@ public class WikibaseSchema implements OverlayModel { final static Logger logger = LoggerFactory.getLogger("RdfSchema"); @JsonProperty("itemDocuments") - protected List itemDocumentExprs = new ArrayList(); + protected List itemDocumentExprs = new ArrayList<>(); - protected String baseIri = "http://www.wikidata.org/entity/"; + @JsonProperty("siteIri") + protected String siteIri; + + @JsonProperty("mediaWikiApiEndpoint") + protected String mediaWikiApiEndpoint; + + @JsonIgnore + protected String editGroupsURLSchema; /** * Constructor. @@ -69,33 +76,45 @@ public class WikibaseSchema implements OverlayModel { public WikibaseSchema() { } - + /** * Constructor for deserialization via Jackson */ @JsonCreator - public WikibaseSchema(@JsonProperty("itemDocuments") List exprs) { + public WikibaseSchema(@JsonProperty("itemDocuments") List exprs, + @JsonProperty("siteIri") String siteIri, + @JsonProperty("mediaWikiApiEndpoint") String mediaWikiApiEndpoint, + @JsonProperty("editGroupsURLSchema") String editGroupsURLSchema) { this.itemDocumentExprs = exprs; + this.siteIri = siteIri; + this.mediaWikiApiEndpoint = mediaWikiApiEndpoint; + this.editGroupsURLSchema = editGroupsURLSchema; } /** * @return the site IRI of the Wikibase instance referenced by this schema */ - @JsonIgnore - public String getBaseIri() { - return baseIri; + @JsonProperty("siteIri") + public String getSiteIri() { + return siteIri; } /** * @return the list of document expressions for this schema */ - @JsonIgnore + @JsonProperty("itemDocuments") public List getItemDocumentExpressions() { return Collections.unmodifiableList(itemDocumentExprs); } - - public void setItemDocumentExpressions(List exprs) { - this.itemDocumentExprs = exprs; + + @JsonProperty("mediaWikiApiEndpoint") + public String getMediaWikiApiEndpoint() { + return mediaWikiApiEndpoint; + } + + @JsonIgnore + public String getEditGroupsURLSchema() { + return editGroupsURLSchema; } /** @@ -168,7 +187,7 @@ public class WikibaseSchema implements OverlayModel { @Override public boolean visit(Project project, int rowIndex, Row row) { - ExpressionContext ctxt = new ExpressionContext(baseIri, rowIndex, row, project.columnModel, warningStore); + ExpressionContext ctxt = new ExpressionContext(siteIri, mediaWikiApiEndpoint, rowIndex, row, project.columnModel, warningStore); result.addAll(evaluateItemDocuments(ctxt)); return false; } diff --git a/extensions/wikidata/src/org/openrefine/wikidata/utils/EntityCache.java b/extensions/wikidata/src/org/openrefine/wikidata/utils/EntityCache.java index 7322bd830..d61fbc939 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/utils/EntityCache.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/utils/EntityCache.java @@ -1,18 +1,18 @@ /******************************************************************************* * MIT License - * + * * Copyright (c) 2018 Antonin Delpeuch - * + * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. - * + * * 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 @@ -26,14 +26,13 @@ package org.openrefine.wikidata.utils; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; -import org.wikidata.wdtk.datamodel.helpers.Datamodel; import org.wikidata.wdtk.datamodel.interfaces.EntityDocument; import org.wikidata.wdtk.datamodel.interfaces.EntityIdValue; -import org.wikidata.wdtk.wikibaseapi.ApiConnection; import org.wikidata.wdtk.wikibaseapi.BasicApiConnection; import org.wikidata.wdtk.wikibaseapi.WikibaseDataFetcher; import org.wikidata.wdtk.wikibaseapi.apierrors.MediaWikiApiErrorException; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; @@ -41,27 +40,25 @@ import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.StreamSupport; + public class EntityCache { - private static EntityCache _entityCache = new EntityCache(BasicApiConnection.getWikidataApiConnection()); + private static Map entityCacheMap = new HashMap<>(); - private LoadingCache _cache = null; - private WikibaseDataFetcher _fetcher; + private LoadingCache cache; - protected EntityCache(ApiConnection connection) { - this(new WikibaseDataFetcher(connection, Datamodel.SITE_WIKIDATA)); + protected EntityCache(String entityPrefix, String mediaWikiApiEndpoint) { + this(new WikibaseDataFetcher(new BasicApiConnection(mediaWikiApiEndpoint), entityPrefix)); } - - protected EntityCache(WikibaseDataFetcher fetcher) { - _fetcher = fetcher; - _cache = CacheBuilder.newBuilder().maximumSize(4096).expireAfterWrite(1, TimeUnit.HOURS) + protected EntityCache(WikibaseDataFetcher fetcher) { + cache = CacheBuilder.newBuilder().maximumSize(4096).expireAfterWrite(1, TimeUnit.HOURS) .build(new CacheLoader() { @Override public EntityDocument load(String entityId) throws Exception { - EntityDocument doc = _fetcher.getEntityDocument(entityId); + EntityDocument doc = fetcher.getEntityDocument(entityId); if (doc != null) { return doc; } else { @@ -72,7 +69,7 @@ public class EntityCache { @Override public Map loadAll(Iterable entityIds) throws Exception { - Map entityDocumentMap = _fetcher.getEntityDocuments(StreamSupport.stream(entityIds.spliterator(), false) + Map entityDocumentMap = fetcher.getEntityDocuments(StreamSupport.stream(entityIds.spliterator(), false) .collect(Collectors.toList())); if (!entityDocumentMap.isEmpty()) { return entityDocumentMap; @@ -85,22 +82,24 @@ public class EntityCache { } public EntityDocument get(EntityIdValue id) { - return _cache.apply(id.getId()); + return cache.apply(id.getId()); } - public static EntityCache getEntityCache() { - if (_entityCache == null) { - _entityCache = new EntityCache(BasicApiConnection.getWikidataApiConnection()); + public static EntityCache getEntityCache(String siteIri, String mediaWikiApiEndpoint) { + EntityCache entityCache = entityCacheMap.get(siteIri); + if (entityCache == null) { + entityCache = new EntityCache(siteIri, mediaWikiApiEndpoint); + entityCacheMap.put(siteIri, entityCache); } - return _entityCache; + return entityCache; } public List getMultipleDocuments(List entityIds) throws ExecutionException { List ids = entityIds.stream().map(entityId -> entityId.getId()).collect(Collectors.toList()); - return _cache.getAll(ids).values().stream().collect(Collectors.toList()); + return cache.getAll(ids).values().stream().collect(Collectors.toList()); } - public static EntityDocument getEntityDocument(EntityIdValue id) { - return getEntityCache().get(id); + public static EntityDocument getEntityDocument(String entityPrefix, String mediaWikiApiEndpoint, EntityIdValue id) { + return getEntityCache(entityPrefix, mediaWikiApiEndpoint).get(id); } } diff --git a/extensions/wikidata/src/org/openrefine/wikidata/utils/LanguageCodeStore.java b/extensions/wikidata/src/org/openrefine/wikidata/utils/LanguageCodeStore.java index 168930e92..b41abeab6 100644 --- a/extensions/wikidata/src/org/openrefine/wikidata/utils/LanguageCodeStore.java +++ b/extensions/wikidata/src/org/openrefine/wikidata/utils/LanguageCodeStore.java @@ -1,15 +1,20 @@ package org.openrefine.wikidata.utils; -import java.util.Arrays; -import java.util.Set; -import java.util.stream.Collectors; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.*; /** * A store for the allowed language code for terms and monolingual text values - * in Wikidata. - * - * @todo generalize for other Wikibase instances (fetch it dynamically via - * https://stackoverflow.com/questions/46507037/how-to-get-all-allowed-languages-for-wikidata/46562061) + * in Wikibase. + * * @todo separate the languages allowed for terms from the ones allowed for monolingual text. * Currently the list is for monolingual texts (which is larger). * @@ -20,7 +25,46 @@ import java.util.stream.Collectors; * */ public class LanguageCodeStore { - public static Set ALLOWED_LANGUAGE_CODES = Arrays.asList( + + private static final Logger logger = LoggerFactory.getLogger(LanguageCodeStore.class); + + private static Map> apiEndpointToLangCodes = new HashMap<>(); + + public static Set getLanguageCodes(String mediaWikiApiEndpoint) { + if (mediaWikiApiEndpoint == null) return DEFAULT_LANGUAGE_CODES; + + if (apiEndpointToLangCodes.containsKey(mediaWikiApiEndpoint)) { + return apiEndpointToLangCodes.get(mediaWikiApiEndpoint); + } + + try { + Set langCodes = fetchLangCodes(mediaWikiApiEndpoint); + apiEndpointToLangCodes.put(mediaWikiApiEndpoint, langCodes); + } catch (IOException e) { + logger.error("An error occurred when fetching language codes from: " + + mediaWikiApiEndpoint + ", fall back to the default language codes", e); + apiEndpointToLangCodes.put(mediaWikiApiEndpoint, DEFAULT_LANGUAGE_CODES); + } + return apiEndpointToLangCodes.get(mediaWikiApiEndpoint); + } + + private static Set fetchLangCodes(String mediaWikiApiEndpoint) throws IOException { + String url = mediaWikiApiEndpoint + + "?action=query&meta=wbcontentlanguages&wbclprop=code&wbclcontext=monolingualtext&format=json"; + OkHttpClient client = new OkHttpClient.Builder().build(); + Request request = new Request.Builder().url(url).build(); + Response response = client.newCall(request).execute(); + JsonNode jsonNode = new ObjectMapper().readTree(response.body().string()); + JsonNode languages = jsonNode.path("query").path("wbcontentlanguages"); + Set supportedLangCodes = new HashSet<>(); + for (JsonNode language : languages) { + supportedLangCodes.add(language.path("code").textValue()); + } + + return supportedLangCodes; + } + + private static Set DEFAULT_LANGUAGE_CODES = new HashSet<>(Arrays.asList( "aa", "ab", "abs", @@ -525,5 +569,5 @@ public class LanguageCodeStore { "xpu", "yap", "zun" - ).stream().collect(Collectors.toSet()); + )); } diff --git a/extensions/wikidata/tests/data/langcode/wikidata-monolingualtext-langcode.json b/extensions/wikidata/tests/data/langcode/wikidata-monolingualtext-langcode.json new file mode 100644 index 000000000..6f14d89ff --- /dev/null +++ b/extensions/wikidata/tests/data/langcode/wikidata-monolingualtext-langcode.json @@ -0,0 +1 @@ +{"batchcomplete":"","query":{"wbcontentlanguages":{"aa":{"code":"aa"},"ab":{"code":"ab"},"abs":{"code":"abs"},"ace":{"code":"ace"},"ady":{"code":"ady"},"ady-cyrl":{"code":"ady-cyrl"},"aeb":{"code":"aeb"},"aeb-arab":{"code":"aeb-arab"},"aeb-latn":{"code":"aeb-latn"},"af":{"code":"af"},"ak":{"code":"ak"},"aln":{"code":"aln"},"als":{"code":"als"},"alt":{"code":"alt"},"am":{"code":"am"},"ami":{"code":"ami"},"an":{"code":"an"},"ang":{"code":"ang"},"anp":{"code":"anp"},"ar":{"code":"ar"},"arc":{"code":"arc"},"arn":{"code":"arn"},"arq":{"code":"arq"},"ary":{"code":"ary"},"arz":{"code":"arz"},"as":{"code":"as"},"ase":{"code":"ase"},"ast":{"code":"ast"},"atj":{"code":"atj"},"av":{"code":"av"},"avk":{"code":"avk"},"awa":{"code":"awa"},"ay":{"code":"ay"},"az":{"code":"az"},"azb":{"code":"azb"},"ba":{"code":"ba"},"ban":{"code":"ban"},"bar":{"code":"bar"},"bbc":{"code":"bbc"},"bbc-latn":{"code":"bbc-latn"},"bcc":{"code":"bcc"},"bcl":{"code":"bcl"},"be":{"code":"be"},"be-tarask":{"code":"be-tarask"},"bg":{"code":"bg"},"bgn":{"code":"bgn"},"bh":{"code":"bh"},"bho":{"code":"bho"},"bi":{"code":"bi"},"bjn":{"code":"bjn"},"bm":{"code":"bm"},"bn":{"code":"bn"},"bo":{"code":"bo"},"bpy":{"code":"bpy"},"bqi":{"code":"bqi"},"br":{"code":"br"},"brh":{"code":"brh"},"bs":{"code":"bs"},"btm":{"code":"btm"},"bto":{"code":"bto"},"bug":{"code":"bug"},"bxr":{"code":"bxr"},"ca":{"code":"ca"},"cbk-zam":{"code":"cbk-zam"},"cdo":{"code":"cdo"},"ce":{"code":"ce"},"ceb":{"code":"ceb"},"ch":{"code":"ch"},"cho":{"code":"cho"},"chr":{"code":"chr"},"chy":{"code":"chy"},"ckb":{"code":"ckb"},"co":{"code":"co"},"cps":{"code":"cps"},"cr":{"code":"cr"},"crh":{"code":"crh"},"crh-cyrl":{"code":"crh-cyrl"},"crh-latn":{"code":"crh-latn"},"cs":{"code":"cs"},"csb":{"code":"csb"},"cu":{"code":"cu"},"cv":{"code":"cv"},"cy":{"code":"cy"},"da":{"code":"da"},"de":{"code":"de"},"de-at":{"code":"de-at"},"de-ch":{"code":"de-ch"},"din":{"code":"din"},"diq":{"code":"diq"},"dsb":{"code":"dsb"},"dtp":{"code":"dtp"},"dty":{"code":"dty"},"dv":{"code":"dv"},"dz":{"code":"dz"},"ee":{"code":"ee"},"egl":{"code":"egl"},"el":{"code":"el"},"eml":{"code":"eml"},"en":{"code":"en"},"en-ca":{"code":"en-ca"},"en-gb":{"code":"en-gb"},"eo":{"code":"eo"},"es":{"code":"es"},"es-419":{"code":"es-419"},"et":{"code":"et"},"eu":{"code":"eu"},"ext":{"code":"ext"},"fa":{"code":"fa"},"ff":{"code":"ff"},"fi":{"code":"fi"},"fit":{"code":"fit"},"fj":{"code":"fj"},"fkv":{"code":"fkv"},"fo":{"code":"fo"},"fr":{"code":"fr"},"frc":{"code":"frc"},"frp":{"code":"frp"},"frr":{"code":"frr"},"fur":{"code":"fur"},"fy":{"code":"fy"},"ga":{"code":"ga"},"gag":{"code":"gag"},"gan":{"code":"gan"},"gan-hans":{"code":"gan-hans"},"gan-hant":{"code":"gan-hant"},"gcr":{"code":"gcr"},"gd":{"code":"gd"},"gl":{"code":"gl"},"glk":{"code":"glk"},"gn":{"code":"gn"},"gom":{"code":"gom"},"gom-deva":{"code":"gom-deva"},"gom-latn":{"code":"gom-latn"},"gor":{"code":"gor"},"got":{"code":"got"},"grc":{"code":"grc"},"gsw":{"code":"gsw"},"gu":{"code":"gu"},"gv":{"code":"gv"},"ha":{"code":"ha"},"hak":{"code":"hak"},"haw":{"code":"haw"},"he":{"code":"he"},"hi":{"code":"hi"},"hif":{"code":"hif"},"hif-latn":{"code":"hif-latn"},"hil":{"code":"hil"},"ho":{"code":"ho"},"hr":{"code":"hr"},"hrx":{"code":"hrx"},"hsb":{"code":"hsb"},"ht":{"code":"ht"},"hu":{"code":"hu"},"hy":{"code":"hy"},"hyw":{"code":"hyw"},"hz":{"code":"hz"},"ia":{"code":"ia"},"id":{"code":"id"},"ie":{"code":"ie"},"ig":{"code":"ig"},"ii":{"code":"ii"},"ik":{"code":"ik"},"ike-cans":{"code":"ike-cans"},"ike-latn":{"code":"ike-latn"},"ilo":{"code":"ilo"},"inh":{"code":"inh"},"io":{"code":"io"},"is":{"code":"is"},"it":{"code":"it"},"iu":{"code":"iu"},"ja":{"code":"ja"},"jam":{"code":"jam"},"jbo":{"code":"jbo"},"jut":{"code":"jut"},"jv":{"code":"jv"},"ka":{"code":"ka"},"kaa":{"code":"kaa"},"kab":{"code":"kab"},"kbd":{"code":"kbd"},"kbd-cyrl":{"code":"kbd-cyrl"},"kbp":{"code":"kbp"},"kea":{"code":"kea"},"kg":{"code":"kg"},"khw":{"code":"khw"},"ki":{"code":"ki"},"kiu":{"code":"kiu"},"kj":{"code":"kj"},"kjp":{"code":"kjp"},"kk":{"code":"kk"},"kk-arab":{"code":"kk-arab"},"kk-cn":{"code":"kk-cn"},"kk-cyrl":{"code":"kk-cyrl"},"kk-kz":{"code":"kk-kz"},"kk-latn":{"code":"kk-latn"},"kk-tr":{"code":"kk-tr"},"kl":{"code":"kl"},"km":{"code":"km"},"kn":{"code":"kn"},"ko":{"code":"ko"},"ko-kp":{"code":"ko-kp"},"koi":{"code":"koi"},"kr":{"code":"kr"},"krc":{"code":"krc"},"kri":{"code":"kri"},"krj":{"code":"krj"},"krl":{"code":"krl"},"ks":{"code":"ks"},"ks-arab":{"code":"ks-arab"},"ks-deva":{"code":"ks-deva"},"ksh":{"code":"ksh"},"ku":{"code":"ku"},"ku-arab":{"code":"ku-arab"},"ku-latn":{"code":"ku-latn"},"kum":{"code":"kum"},"kv":{"code":"kv"},"kw":{"code":"kw"},"ky":{"code":"ky"},"la":{"code":"la"},"lad":{"code":"lad"},"lb":{"code":"lb"},"lbe":{"code":"lbe"},"lez":{"code":"lez"},"lfn":{"code":"lfn"},"lg":{"code":"lg"},"li":{"code":"li"},"lij":{"code":"lij"},"liv":{"code":"liv"},"lki":{"code":"lki"},"lld":{"code":"lld"},"lmo":{"code":"lmo"},"ln":{"code":"ln"},"lo":{"code":"lo"},"loz":{"code":"loz"},"lrc":{"code":"lrc"},"lt":{"code":"lt"},"ltg":{"code":"ltg"},"lus":{"code":"lus"},"luz":{"code":"luz"},"lv":{"code":"lv"},"lzh":{"code":"lzh"},"lzz":{"code":"lzz"},"mai":{"code":"mai"},"map-bms":{"code":"map-bms"},"mdf":{"code":"mdf"},"mg":{"code":"mg"},"mh":{"code":"mh"},"mhr":{"code":"mhr"},"mi":{"code":"mi"},"min":{"code":"min"},"mk":{"code":"mk"},"ml":{"code":"ml"},"mn":{"code":"mn"},"mni":{"code":"mni"},"mnw":{"code":"mnw"},"mo":{"code":"mo"},"mr":{"code":"mr"},"mrj":{"code":"mrj"},"ms":{"code":"ms"},"mt":{"code":"mt"},"mus":{"code":"mus"},"mwl":{"code":"mwl"},"my":{"code":"my"},"myv":{"code":"myv"},"mzn":{"code":"mzn"},"na":{"code":"na"},"nah":{"code":"nah"},"nan":{"code":"nan"},"nap":{"code":"nap"},"nb":{"code":"nb"},"nds":{"code":"nds"},"nds-nl":{"code":"nds-nl"},"ne":{"code":"ne"},"new":{"code":"new"},"ng":{"code":"ng"},"niu":{"code":"niu"},"nl":{"code":"nl"},"nn":{"code":"nn"},"no":{"code":"no"},"nod":{"code":"nod"},"nov":{"code":"nov"},"nqo":{"code":"nqo"},"nrm":{"code":"nrm"},"nso":{"code":"nso"},"nv":{"code":"nv"},"ny":{"code":"ny"},"nys":{"code":"nys"},"oc":{"code":"oc"},"olo":{"code":"olo"},"om":{"code":"om"},"or":{"code":"or"},"os":{"code":"os"},"ota":{"code":"ota"},"pa":{"code":"pa"},"pag":{"code":"pag"},"pam":{"code":"pam"},"pap":{"code":"pap"},"pcd":{"code":"pcd"},"pdc":{"code":"pdc"},"pdt":{"code":"pdt"},"pfl":{"code":"pfl"},"pi":{"code":"pi"},"pih":{"code":"pih"},"pl":{"code":"pl"},"pms":{"code":"pms"},"pnb":{"code":"pnb"},"pnt":{"code":"pnt"},"prg":{"code":"prg"},"ps":{"code":"ps"},"pt":{"code":"pt"},"pt-br":{"code":"pt-br"},"qu":{"code":"qu"},"qug":{"code":"qug"},"rgn":{"code":"rgn"},"rif":{"code":"rif"},"rm":{"code":"rm"},"rmf":{"code":"rmf"},"rmy":{"code":"rmy"},"rn":{"code":"rn"},"ro":{"code":"ro"},"roa-tara":{"code":"roa-tara"},"ru":{"code":"ru"},"rue":{"code":"rue"},"rup":{"code":"rup"},"ruq":{"code":"ruq"},"ruq-cyrl":{"code":"ruq-cyrl"},"ruq-latn":{"code":"ruq-latn"},"rw":{"code":"rw"},"rwr":{"code":"rwr"},"sa":{"code":"sa"},"sah":{"code":"sah"},"sat":{"code":"sat"},"sc":{"code":"sc"},"scn":{"code":"scn"},"sco":{"code":"sco"},"sd":{"code":"sd"},"sdc":{"code":"sdc"},"sdh":{"code":"sdh"},"se":{"code":"se"},"sei":{"code":"sei"},"ses":{"code":"ses"},"sg":{"code":"sg"},"sgs":{"code":"sgs"},"sh":{"code":"sh"},"shi":{"code":"shi"},"shi-latn":{"code":"shi-latn"},"shi-tfng":{"code":"shi-tfng"},"shn":{"code":"shn"},"shy-latn":{"code":"shy-latn"},"si":{"code":"si"},"sjd":{"code":"sjd"},"sje":{"code":"sje"},"sju":{"code":"sju"},"sk":{"code":"sk"},"skr":{"code":"skr"},"skr-arab":{"code":"skr-arab"},"sl":{"code":"sl"},"sli":{"code":"sli"},"sm":{"code":"sm"},"sma":{"code":"sma"},"smj":{"code":"smj"},"smn":{"code":"smn"},"sms":{"code":"sms"},"sn":{"code":"sn"},"so":{"code":"so"},"sq":{"code":"sq"},"sr":{"code":"sr"},"sr-ec":{"code":"sr-ec"},"sr-el":{"code":"sr-el"},"srn":{"code":"srn"},"srq":{"code":"srq"},"ss":{"code":"ss"},"st":{"code":"st"},"stq":{"code":"stq"},"sty":{"code":"sty"},"su":{"code":"su"},"sv":{"code":"sv"},"sw":{"code":"sw"},"szl":{"code":"szl"},"szy":{"code":"szy"},"ta":{"code":"ta"},"tay":{"code":"tay"},"tcy":{"code":"tcy"},"te":{"code":"te"},"tet":{"code":"tet"},"tg":{"code":"tg"},"tg-cyrl":{"code":"tg-cyrl"},"tg-latn":{"code":"tg-latn"},"th":{"code":"th"},"ti":{"code":"ti"},"tk":{"code":"tk"},"tl":{"code":"tl"},"tly":{"code":"tly"},"tn":{"code":"tn"},"to":{"code":"to"},"tpi":{"code":"tpi"},"tr":{"code":"tr"},"tru":{"code":"tru"},"trv":{"code":"trv"},"ts":{"code":"ts"},"tt":{"code":"tt"},"tt-cyrl":{"code":"tt-cyrl"},"tt-latn":{"code":"tt-latn"},"tum":{"code":"tum"},"tw":{"code":"tw"},"ty":{"code":"ty"},"tyv":{"code":"tyv"},"tzm":{"code":"tzm"},"udm":{"code":"udm"},"ug":{"code":"ug"},"ug-arab":{"code":"ug-arab"},"ug-latn":{"code":"ug-latn"},"uk":{"code":"uk"},"ur":{"code":"ur"},"uz":{"code":"uz"},"uz-cyrl":{"code":"uz-cyrl"},"uz-latn":{"code":"uz-latn"},"ve":{"code":"ve"},"vec":{"code":"vec"},"vep":{"code":"vep"},"vi":{"code":"vi"},"vls":{"code":"vls"},"vmf":{"code":"vmf"},"vo":{"code":"vo"},"vot":{"code":"vot"},"vro":{"code":"vro"},"wa":{"code":"wa"},"war":{"code":"war"},"wo":{"code":"wo"},"wuu":{"code":"wuu"},"xal":{"code":"xal"},"xh":{"code":"xh"},"xmf":{"code":"xmf"},"xsy":{"code":"xsy"},"yi":{"code":"yi"},"yo":{"code":"yo"},"yue":{"code":"yue"},"za":{"code":"za"},"zea":{"code":"zea"},"zgh":{"code":"zgh"},"zh":{"code":"zh"},"zh-cn":{"code":"zh-cn"},"zh-hans":{"code":"zh-hans"},"zh-hant":{"code":"zh-hant"},"zh-hk":{"code":"zh-hk"},"zh-mo":{"code":"zh-mo"},"zh-my":{"code":"zh-my"},"zh-sg":{"code":"zh-sg"},"zh-tw":{"code":"zh-tw"},"zu":{"code":"zu"},"und":{"code":"und"},"mis":{"code":"mis"},"mul":{"code":"mul"},"zxx":{"code":"zxx"},"abe":{"code":"abe"},"abq":{"code":"abq"},"abq-latn":{"code":"abq-latn"},"alc":{"code":"alc"},"bdr":{"code":"bdr"},"bnn":{"code":"bnn"},"brx":{"code":"brx"},"ccp":{"code":"ccp"},"chn":{"code":"chn"},"ckt":{"code":"ckt"},"clc":{"code":"clc"},"cnr":{"code":"cnr"},"cop":{"code":"cop"},"crb":{"code":"crb"},"dag":{"code":"dag"},"el-cy":{"code":"el-cy"},"ett":{"code":"ett"},"eya":{"code":"eya"},"fos":{"code":"fos"},"fr-ca":{"code":"fr-ca"},"frm":{"code":"frm"},"fro":{"code":"fro"},"fuf":{"code":"fuf"},"gez":{"code":"gez"},"gil":{"code":"gil"},"gmy":{"code":"gmy"},"hai":{"code":"hai"},"haz":{"code":"haz"},"hbo":{"code":"hbo"},"kjh":{"code":"kjh"},"kld":{"code":"kld"},"koy":{"code":"koy"},"lag":{"code":"lag"},"lcm":{"code":"lcm"},"lkt":{"code":"lkt"},"mfa":{"code":"mfa"},"mic":{"code":"mic"},"mid":{"code":"mid"},"mnc":{"code":"mnc"},"moe":{"code":"moe"},"non":{"code":"non"},"nr":{"code":"nr"},"nrf-gg":{"code":"nrf-gg"},"nrf-je":{"code":"nrf-je"},"nsk":{"code":"nsk"},"nxm":{"code":"nxm"},"ood":{"code":"ood"},"otk":{"code":"otk"},"peo":{"code":"peo"},"pi-sidd":{"code":"pi-sidd"},"pjt":{"code":"pjt"},"ppu":{"code":"ppu"},"pwn":{"code":"pwn"},"pyu":{"code":"pyu"},"quc":{"code":"quc"},"qya":{"code":"qya"},"rar":{"code":"rar"},"rm-puter":{"code":"rm-puter"},"rm-rumgr":{"code":"rm-rumgr"},"rm-surmiran":{"code":"rm-surmiran"},"rm-sursilv":{"code":"rm-sursilv"},"rm-sutsilv":{"code":"rm-sutsilv"},"rm-vallader":{"code":"rm-vallader"},"sa-sidd":{"code":"sa-sidd"},"shy":{"code":"shy"},"sia":{"code":"sia"},"sjk":{"code":"sjk"},"sjn":{"code":"sjn"},"sjt":{"code":"sjt"},"ssf":{"code":"ssf"},"syc":{"code":"syc"},"tlb":{"code":"tlb"},"tli":{"code":"tli"},"tnq":{"code":"tnq"},"tzl":{"code":"tzl"},"uga":{"code":"uga"},"umu":{"code":"umu"},"uun":{"code":"uun"},"wls":{"code":"wls"},"xpu":{"code":"xpu"},"yap":{"code":"yap"},"zun":{"code":"zun"}}}} diff --git a/extensions/wikidata/tests/data/manifest/wikidata-manifest-v1.0-missing-property-constraint-pid.json b/extensions/wikidata/tests/data/manifest/wikidata-manifest-v1.0-missing-property-constraint-pid.json new file mode 100644 index 000000000..0ca459647 --- /dev/null +++ b/extensions/wikidata/tests/data/manifest/wikidata-manifest-v1.0-missing-property-constraint-pid.json @@ -0,0 +1,91 @@ +{ + "version": "1.0", + "mediawiki": { + "name": "Wikidata", + "root": "https://www.wikidata.org/wiki/", + "main_page": "https://www.wikidata.org/wiki/Wikidata:Main_Page", + "api": "https://www.wikidata.org/w/api.php" + }, + "wikibase": { + "site_iri": "http://www.wikidata.org/entity/", + "maxlag": 5, + "properties": { + "instance_of": "P31", + "subclass_of": "P279" + }, + "constraints": { + "exception_to_constraint_pid": "P2303", + "constraint_status_pid": "P2316", + "mandatory_constraint_qid": "Q21502408", + "suggestion_constraint_qid": "Q62026391", + "distinct_values_constraint_qid": "Q21502410", + "multi_value_constraint_qid": "Q21510857", + "used_as_qualifier_constraint_qid": "Q21510863", + "single_value_constraint_qid": "Q19474404", + "symmetric_constraint_qid": "Q21510862", + "type_constraint_qid": "Q21503250", + "value_type_constraint_qid": "Q21510865", + "inverse_constraint_qid": "Q21510855", + "item_requires_statement_constraint_qid": "Q21503247", + "value_requires_statement_constraint_qid": "Q21510864", + "conflicts_with_constraint_qid": "Q21502838", + "one_of_constraint_qid": "Q21510859", + "mandatory_qualifier_constraint_qid": "Q21510856", + "allowed_qualifiers_constraint_qid": "Q21510851", + "range_constraint_qid": "Q21510860", + "difference_within_range_constraint_qid": "Q21510854", + "common_link_constraint_qid": "Q21510852", + "contemporary_constraint_qid": "Q25796498", + "format_constraint_qid": "Q21502404", + "used_for_values_only_constraint_qid": "Q21528958", + "used_as_reference_constraint_qid": "Q21528959", + "no_bounds_constraint_qid": "Q51723761", + "allowed_units_constraint_qid": "Q21514353", + "single_best_value_constraint_qid": "Q52060874", + "allowed_entity_types_constraint_qid": "Q52004125", + "citation_needed_constraint_qid": "Q54554025", + "property_scope_constraint_qid": "Q53869507", + "class_pid": "P2308", + "relation_pid": "P2309", + "instance_of_relation_qid": "Q21503252", + "subclass_of_relation_qid": "Q21514624", + "instance_or_subclass_of_relation_qid": "Q30208840", + "property_pid": "P2306", + "item_of_property_constraint_pid": "P2305", + "minimum_value_pid": "P2313", + "maximum_value_pid": "P2312", + "minimum_date_pid": "P2310", + "maximum_date_pid": "P2311", + "namespace_pid": "P2307", + "format_as_a_regular_expression_pid": "P1793", + "syntax_clarification_pid": "P2916", + "constraint_scope_pid": "P4680", + "separator_pid": "P4155", + "constraint_checked_on_main_value_qid": "Q46466787", + "constraint_checked_on_qualifiers_qid": "Q46466783", + "constraint_checked_on_references_qid": "Q46466805", + "none_of_constraint_qid": "Q52558054", + "one_of_qualifier_value_property_constraint_qid": "Q52712340", + "integer_constraint_qid": "Q52848401", + "wikibase_item_qid": "Q29934200", + "wikibase_property_qid": "Q29934218", + "wikibase_lexeme_qid": "Q51885771", + "wikibase_form_qid": "Q54285143", + "wikibase_sense_qid": "Q54285715", + "wikibase_media_info_qid": "Q59712033", + "property_scope_pid": "P5314", + "as_main_value_qid": "Q54828448", + "as_qualifiers_qid": "Q54828449", + "as_references_qid": "Q54828450" + } + }, + "oauth": { + "registration_page": "https://meta.wikimedia.org/wiki/Special:OAuthConsumerRegistration/propose" + }, + "reconciliation": { + "endpoint": "https://wdreconcile.toolforge.org/${lang}/api" + }, + "editgroups": { + "url_schema": "([[:toollabs:editgroups/b/OR/${batch_id}|details]])" + } +} diff --git a/extensions/wikidata/tests/data/manifest/wikidata-manifest-v1.0-without-constraints.json b/extensions/wikidata/tests/data/manifest/wikidata-manifest-v1.0-without-constraints.json new file mode 100644 index 000000000..b57296b46 --- /dev/null +++ b/extensions/wikidata/tests/data/manifest/wikidata-manifest-v1.0-without-constraints.json @@ -0,0 +1,26 @@ +{ + "version": "1.0", + "mediawiki": { + "name": "Wikidata", + "root": "https://www.wikidata.org/wiki/", + "main_page": "https://www.wikidata.org/wiki/Wikidata:Main_Page", + "api": "https://www.wikidata.org/w/api.php" + }, + "wikibase": { + "site_iri": "http://www.wikidata.org/entity/", + "maxlag": 5, + "properties": { + "instance_of": "P31", + "subclass_of": "P279" + } + }, + "oauth": { + "registration_page": "https://meta.wikimedia.org/wiki/Special:OAuthConsumerRegistration/propose" + }, + "reconciliation": { + "endpoint": "https://wdreconcile.toolforge.org/${lang}/api" + }, + "editgroups": { + "url_schema": "([[:toollabs:editgroups/b/OR/${batch_id}|details]])" + } +} diff --git a/extensions/wikidata/tests/data/manifest/wikidata-manifest-v1.0.json b/extensions/wikidata/tests/data/manifest/wikidata-manifest-v1.0.json new file mode 100644 index 000000000..95f9b8483 --- /dev/null +++ b/extensions/wikidata/tests/data/manifest/wikidata-manifest-v1.0.json @@ -0,0 +1,92 @@ +{ + "version": "1.0", + "mediawiki": { + "name": "Wikidata", + "root": "https://www.wikidata.org/wiki/", + "main_page": "https://www.wikidata.org/wiki/Wikidata:Main_Page", + "api": "https://www.wikidata.org/w/api.php" + }, + "wikibase": { + "site_iri": "http://www.wikidata.org/entity/", + "maxlag": 5, + "properties": { + "instance_of": "P31", + "subclass_of": "P279" + }, + "constraints": { + "property_constraint_pid": "P2302", + "exception_to_constraint_pid": "P2303", + "constraint_status_pid": "P2316", + "mandatory_constraint_qid": "Q21502408", + "suggestion_constraint_qid": "Q62026391", + "distinct_values_constraint_qid": "Q21502410", + "multi_value_constraint_qid": "Q21510857", + "used_as_qualifier_constraint_qid": "Q21510863", + "single_value_constraint_qid": "Q19474404", + "symmetric_constraint_qid": "Q21510862", + "type_constraint_qid": "Q21503250", + "value_type_constraint_qid": "Q21510865", + "inverse_constraint_qid": "Q21510855", + "item_requires_statement_constraint_qid": "Q21503247", + "value_requires_statement_constraint_qid": "Q21510864", + "conflicts_with_constraint_qid": "Q21502838", + "one_of_constraint_qid": "Q21510859", + "mandatory_qualifier_constraint_qid": "Q21510856", + "allowed_qualifiers_constraint_qid": "Q21510851", + "range_constraint_qid": "Q21510860", + "difference_within_range_constraint_qid": "Q21510854", + "common_link_constraint_qid": "Q21510852", + "contemporary_constraint_qid": "Q25796498", + "format_constraint_qid": "Q21502404", + "used_for_values_only_constraint_qid": "Q21528958", + "used_as_reference_constraint_qid": "Q21528959", + "no_bounds_constraint_qid": "Q51723761", + "allowed_units_constraint_qid": "Q21514353", + "single_best_value_constraint_qid": "Q52060874", + "allowed_entity_types_constraint_qid": "Q52004125", + "citation_needed_constraint_qid": "Q54554025", + "property_scope_constraint_qid": "Q53869507", + "class_pid": "P2308", + "relation_pid": "P2309", + "instance_of_relation_qid": "Q21503252", + "subclass_of_relation_qid": "Q21514624", + "instance_or_subclass_of_relation_qid": "Q30208840", + "property_pid": "P2306", + "item_of_property_constraint_pid": "P2305", + "minimum_value_pid": "P2313", + "maximum_value_pid": "P2312", + "minimum_date_pid": "P2310", + "maximum_date_pid": "P2311", + "namespace_pid": "P2307", + "format_as_a_regular_expression_pid": "P1793", + "syntax_clarification_pid": "P2916", + "constraint_scope_pid": "P4680", + "separator_pid": "P4155", + "constraint_checked_on_main_value_qid": "Q46466787", + "constraint_checked_on_qualifiers_qid": "Q46466783", + "constraint_checked_on_references_qid": "Q46466805", + "none_of_constraint_qid": "Q52558054", + "one_of_qualifier_value_property_constraint_qid": "Q52712340", + "integer_constraint_qid": "Q52848401", + "wikibase_item_qid": "Q29934200", + "wikibase_property_qid": "Q29934218", + "wikibase_lexeme_qid": "Q51885771", + "wikibase_form_qid": "Q54285143", + "wikibase_sense_qid": "Q54285715", + "wikibase_media_info_qid": "Q59712033", + "property_scope_pid": "P5314", + "as_main_value_qid": "Q54828448", + "as_qualifiers_qid": "Q54828449", + "as_references_qid": "Q54828450" + } + }, + "oauth": { + "registration_page": "https://meta.wikimedia.org/wiki/Special:OAuthConsumerRegistration/propose" + }, + "reconciliation": { + "endpoint": "https://wdreconcile.toolforge.org/${lang}/api" + }, + "editgroups": { + "url_schema": "([[:toollabs:editgroups/b/OR/${batch_id}|details]])" + } +} diff --git a/extensions/wikidata/tests/data/operations/perform-edits.json b/extensions/wikidata/tests/data/operations/perform-edits.json index 403c136cf..0d6ded5c3 100644 --- a/extensions/wikidata/tests/data/operations/perform-edits.json +++ b/extensions/wikidata/tests/data/operations/perform-edits.json @@ -2,6 +2,7 @@ "op": "wikidata/perform-wikibase-edits", "description": "Perform Wikibase edits", "summary": "test null edit", + "maxlag": 5, "engineConfig": { "mode": "row-based", "facets": [] diff --git a/extensions/wikidata/tests/data/operations/save-schema.json b/extensions/wikidata/tests/data/operations/save-schema.json index 5514212ab..5b8c78d62 100644 --- a/extensions/wikidata/tests/data/operations/save-schema.json +++ b/extensions/wikidata/tests/data/operations/save-schema.json @@ -28,6 +28,8 @@ } ] } - ] + ], + "siteIri": "http://www.wikidata.org/entity/", + "mediaWikiApiEndpoint":"https://www.wikidata.org/w/api.php" } } diff --git a/extensions/wikidata/tests/data/schema/history_of_medicine.json b/extensions/wikidata/tests/data/schema/history_of_medicine.json index 6c152dd49..f8401fa32 100644 --- a/extensions/wikidata/tests/data/schema/history_of_medicine.json +++ b/extensions/wikidata/tests/data/schema/history_of_medicine.json @@ -1 +1 @@ -{"itemDocuments":[{"subject":{"type":"wbitemvariable","columnName":"name"},"statementGroups":[{"property":{"datatype":"wikibase-item","pid":"P39","label":"position held","type":"wbpropconstant"},"statements":[{"value":{"type":"wbitemconstant","qid":"Q30461","label":"president"},"qualifiers":[{"prop":{"datatype":"wikibase-item","pid":"P642","label":"of","type":"wbpropconstant"},"value":{"label":"History of Medicine Society","type":"wbitemconstant","qid":"Q37461404"}},{"prop":{"datatype":"time","pid":"P580","label":"start date","type":"wbpropconstant"},"value":{"type":"wbdatevariable","columnName":"start"}},{"prop":{"datatype":"time","pid":"P582","label":"end date","type":"wbpropconstant"},"value":{"type":"wbdatevariable","columnName":"end"}}],"references":[{"snaks":[{"prop":{"datatype":"wikibase-item","pid":"P248","label":"stated in","type":"wbpropconstant"},"value":{"label":"The History of The Royal Society of Medicine","type":"wbitemconstant","qid":"Q42036099"}},{"prop":{"datatype":"string","pid":"P304","label":"page","type":"wbpropconstant"},"value":{"type":"wbstringconstant","value":"330-333"}}]}]}]}],"nameDescs":[]},{"subject":{"label":"History of Medicine Society","type":"wbitemconstant","qid":"Q37461404"},"statementGroups":[{"property":{"datatype":"wikibase-item","pid":"P488","label":"chairperson","type":"wbpropconstant"},"statements":[{"value":{"type":"wbitemvariable","columnName":"name"},"qualifiers":[{"prop":{"datatype":"time","pid":"P580","label":"start date","type":"wbpropconstant"},"value":{"type":"wbdatevariable","columnName":"start"}},{"prop":{"datatype":"time","pid":"P582","label":"end date","type":"wbpropconstant"},"value":{"type":"wbdatevariable","columnName":"end"}}],"references":[{"snaks":[{"prop":{"datatype":"wikibase-item","pid":"P248","label":"stated in","type":"wbpropconstant"},"value":{"label":"The History of The Royal Society of Medicine","type":"wbitemconstant","qid":"Q42036099"}},{"prop":{"datatype":"string","pid":"P304","label":"page","type":"wbpropconstant"},"value":{"type":"wbstringconstant","value":"330-333"}}]}]}]}],"nameDescs":[]}],"wikibasePrefix":"http://www.wikidata.org/entity/"} +{"itemDocuments":[{"subject":{"type":"wbitemvariable","columnName":"name"},"statementGroups":[{"property":{"datatype":"wikibase-item","pid":"P39","label":"position held","type":"wbpropconstant"},"statements":[{"value":{"type":"wbitemconstant","qid":"Q30461","label":"president"},"qualifiers":[{"prop":{"datatype":"wikibase-item","pid":"P642","label":"of","type":"wbpropconstant"},"value":{"label":"History of Medicine Society","type":"wbitemconstant","qid":"Q37461404"}},{"prop":{"datatype":"time","pid":"P580","label":"start date","type":"wbpropconstant"},"value":{"type":"wbdatevariable","columnName":"start"}},{"prop":{"datatype":"time","pid":"P582","label":"end date","type":"wbpropconstant"},"value":{"type":"wbdatevariable","columnName":"end"}}],"references":[{"snaks":[{"prop":{"datatype":"wikibase-item","pid":"P248","label":"stated in","type":"wbpropconstant"},"value":{"label":"The History of The Royal Society of Medicine","type":"wbitemconstant","qid":"Q42036099"}},{"prop":{"datatype":"string","pid":"P304","label":"page","type":"wbpropconstant"},"value":{"type":"wbstringconstant","value":"330-333"}}]}]}]}],"nameDescs":[]},{"subject":{"label":"History of Medicine Society","type":"wbitemconstant","qid":"Q37461404"},"statementGroups":[{"property":{"datatype":"wikibase-item","pid":"P488","label":"chairperson","type":"wbpropconstant"},"statements":[{"value":{"type":"wbitemvariable","columnName":"name"},"qualifiers":[{"prop":{"datatype":"time","pid":"P580","label":"start date","type":"wbpropconstant"},"value":{"type":"wbdatevariable","columnName":"start"}},{"prop":{"datatype":"time","pid":"P582","label":"end date","type":"wbpropconstant"},"value":{"type":"wbdatevariable","columnName":"end"}}],"references":[{"snaks":[{"prop":{"datatype":"wikibase-item","pid":"P248","label":"stated in","type":"wbpropconstant"},"value":{"label":"The History of The Royal Society of Medicine","type":"wbitemconstant","qid":"Q42036099"}},{"prop":{"datatype":"string","pid":"P304","label":"page","type":"wbpropconstant"},"value":{"type":"wbstringconstant","value":"330-333"}}]}]}]}],"nameDescs":[]}],"siteIri":"http://www.wikidata.org/entity/","mediaWikiApiEndpoint":"https://www.wikidata.org/w/api.php"} diff --git a/extensions/wikidata/tests/data/schema/history_of_medicine_normalized.json b/extensions/wikidata/tests/data/schema/history_of_medicine_normalized.json index 8e729cd11..8ef811097 100644 --- a/extensions/wikidata/tests/data/schema/history_of_medicine_normalized.json +++ b/extensions/wikidata/tests/data/schema/history_of_medicine_normalized.json @@ -1 +1 @@ -{"itemDocuments":[{"subject":{"type":"wbitemvariable","columnName":"name"},"statementGroups":[{"property":{"datatype":"wikibase-item","pid":"P39","label":"position held","type":"wbpropconstant"},"statements":[{"references":[{"snaks":[{"prop":{"datatype":"wikibase-item","pid":"P248","label":"stated in","type":"wbpropconstant"},"value":{"label":"The History of The Royal Society of Medicine","type":"wbitemconstant","qid":"Q42036099"}},{"prop":{"datatype":"string","pid":"P304","label":"page","type":"wbpropconstant"},"value":{"type":"wbstringconstant","value":"330-333"}}]}],"qualifiers":[{"prop":{"datatype":"wikibase-item","pid":"P642","label":"of","type":"wbpropconstant"},"value":{"label":"History of Medicine Society","type":"wbitemconstant","qid":"Q37461404"}},{"prop":{"datatype":"time","pid":"P580","label":"start date","type":"wbpropconstant"},"value":{"type":"wbdatevariable","columnName":"start"}},{"prop":{"datatype":"time","pid":"P582","label":"end date","type":"wbpropconstant"},"value":{"type":"wbdatevariable","columnName":"end"}}],"value":{"label":"president","type":"wbitemconstant","qid":"Q30461"}}]}],"nameDescs":[]},{"subject":{"label":"History of Medicine Society","type":"wbitemconstant","qid":"Q37461404"},"statementGroups":[{"property":{"datatype":"wikibase-item","pid":"P488","label":"chairperson","type":"wbpropconstant"},"statements":[{"references":[{"snaks":[{"prop":{"datatype":"wikibase-item","pid":"P248","label":"stated in","type":"wbpropconstant"},"value":{"label":"The History of The Royal Society of Medicine","type":"wbitemconstant","qid":"Q42036099"}},{"prop":{"datatype":"string","pid":"P304","label":"page","type":"wbpropconstant"},"value":{"type":"wbstringconstant","value":"330-333"}}]}],"qualifiers":[{"prop":{"datatype":"time","pid":"P580","label":"start date","type":"wbpropconstant"},"value":{"type":"wbdatevariable","columnName":"start"}},{"prop":{"datatype":"time","pid":"P582","label":"end date","type":"wbpropconstant"},"value":{"type":"wbdatevariable","columnName":"end"}}],"value":{"type":"wbitemvariable","columnName":"name"}}]}],"nameDescs":[]}]} +{"itemDocuments":[{"subject":{"type":"wbitemvariable","columnName":"name"},"statementGroups":[{"property":{"datatype":"wikibase-item","pid":"P39","label":"position held","type":"wbpropconstant"},"statements":[{"references":[{"snaks":[{"prop":{"datatype":"wikibase-item","pid":"P248","label":"stated in","type":"wbpropconstant"},"value":{"label":"The History of The Royal Society of Medicine","type":"wbitemconstant","qid":"Q42036099"}},{"prop":{"datatype":"string","pid":"P304","label":"page","type":"wbpropconstant"},"value":{"type":"wbstringconstant","value":"330-333"}}]}],"qualifiers":[{"prop":{"datatype":"wikibase-item","pid":"P642","label":"of","type":"wbpropconstant"},"value":{"label":"History of Medicine Society","type":"wbitemconstant","qid":"Q37461404"}},{"prop":{"datatype":"time","pid":"P580","label":"start date","type":"wbpropconstant"},"value":{"type":"wbdatevariable","columnName":"start"}},{"prop":{"datatype":"time","pid":"P582","label":"end date","type":"wbpropconstant"},"value":{"type":"wbdatevariable","columnName":"end"}}],"value":{"label":"president","type":"wbitemconstant","qid":"Q30461"}}]}],"nameDescs":[]},{"subject":{"label":"History of Medicine Society","type":"wbitemconstant","qid":"Q37461404"},"statementGroups":[{"property":{"datatype":"wikibase-item","pid":"P488","label":"chairperson","type":"wbpropconstant"},"statements":[{"references":[{"snaks":[{"prop":{"datatype":"wikibase-item","pid":"P248","label":"stated in","type":"wbpropconstant"},"value":{"label":"The History of The Royal Society of Medicine","type":"wbitemconstant","qid":"Q42036099"}},{"prop":{"datatype":"string","pid":"P304","label":"page","type":"wbpropconstant"},"value":{"type":"wbstringconstant","value":"330-333"}}]}],"qualifiers":[{"prop":{"datatype":"time","pid":"P580","label":"start date","type":"wbpropconstant"},"value":{"type":"wbdatevariable","columnName":"start"}},{"prop":{"datatype":"time","pid":"P582","label":"end date","type":"wbpropconstant"},"value":{"type":"wbdatevariable","columnName":"end"}}],"value":{"type":"wbitemvariable","columnName":"name"}}]}],"nameDescs":[]}],"siteIri":"http://www.wikidata.org/entity/","mediaWikiApiEndpoint":"https://www.wikidata.org/w/api.php"} diff --git a/extensions/wikidata/tests/data/schema/inception.json b/extensions/wikidata/tests/data/schema/inception.json index a7d90cdc8..8ce46a821 100644 --- a/extensions/wikidata/tests/data/schema/inception.json +++ b/extensions/wikidata/tests/data/schema/inception.json @@ -1 +1 @@ -{"itemDocuments":[{"subject":{"type":"wbitemvariable","columnName":"subject"},"statementGroups":[{"property":{"type":"wbpropconstant","pid":"P571","label":"inception","datatype":"time"},"statements":[{"value":{"type":"wbdatevariable","columnName":"inception"},"qualifiers":[],"references":[{"snaks":[{"prop":{"type":"wbpropconstant","pid":"P854","label":"reference URL","datatype":"url"},"value":{"type":"wbstringvariable","columnName":"reference"}},{"prop":{"type":"wbpropconstant","pid":"P813","label":"retrieved","datatype":"time"},"value":{"type":"wbdateconstant","value":"2018-02-28"}}]}]}]}],"nameDescs":[]}],"wikibasePrefix":"http://www.wikidata.org/entity/"} +{"itemDocuments":[{"subject":{"type":"wbitemvariable","columnName":"subject"},"statementGroups":[{"property":{"type":"wbpropconstant","pid":"P571","label":"inception","datatype":"time"},"statements":[{"value":{"type":"wbdatevariable","columnName":"inception"},"qualifiers":[],"references":[{"snaks":[{"prop":{"type":"wbpropconstant","pid":"P854","label":"reference URL","datatype":"url"},"value":{"type":"wbstringvariable","columnName":"reference"}},{"prop":{"type":"wbpropconstant","pid":"P813","label":"retrieved","datatype":"time"},"value":{"type":"wbdateconstant","value":"2018-02-28"}}]}]}]}],"nameDescs":[]}],"siteIri":"http://www.wikidata.org/entity/"} diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/commands/LoginCommandTest.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/commands/LoginCommandTest.java index 9957fbdcb..a7b18e1f1 100644 --- a/extensions/wikidata/tests/src/org/openrefine/wikidata/commands/LoginCommandTest.java +++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/commands/LoginCommandTest.java @@ -39,6 +39,9 @@ import static org.testng.Assert.*; @PrepareForTest(ConnectionManager.class) public class LoginCommandTest extends CommandTest { + private static final String apiEndpoint = "https://www.wikidata.org/w/api.php"; + private static final String apiEndpointPrefix = apiEndpoint + "-"; + private static final String username = "my_username"; private static final String password = "my_password"; @@ -100,11 +103,24 @@ public class LoginCommandTest extends CommandTest { } @Test - public void testNoCredentials() throws ServletException, IOException { + public void testNoApiEndpointPost() throws ServletException, IOException { when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken()); command.doPost(request, response); - // the first param is the actual one for testng.assertEquals - assertEquals(writer.toString(), "{\"logged_in\":false,\"username\":null}"); + assertEqualAsJson("{\"code\":\"error\",\"message\":\"missing parameter 'wb-api-endpoint'\"}", writer.toString()); + } + + @Test + public void testNoApiEndpointGet() throws ServletException, IOException { + command.doGet(request, response); + assertEqualAsJson("{\"code\":\"error\",\"message\":\"missing parameter 'wb-api-endpoint'\"}", writer.toString()); + } + + @Test + public void testNoCredentials() throws ServletException, IOException { + when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken()); + when(request.getParameter(API_ENDPOINT)).thenReturn(apiEndpoint); + command.doPost(request, response); + assertEqualAsJson("{\"logged_in\":false,\"username\":null,\"mediawiki_api_endpoint\":\"" + apiEndpoint + "\"}", writer.toString()); } @Test @@ -115,8 +131,15 @@ public class LoginCommandTest extends CommandTest { @Test public void testGetNotCsrfProtected() throws ServletException, IOException { + when(request.getParameter(API_ENDPOINT)).thenReturn(apiEndpoint); command.doGet(request, response); - assertEqualAsJson("{\"logged_in\":false,\"username\":null}", writer.toString()); + assertEqualAsJson("{\"logged_in\":false,\"username\":null,\"mediawiki_api_endpoint\":\"" + apiEndpoint + "\"}", writer.toString()); + } + + private void assertLogin() { + assertTrue(ConnectionManager.getInstance().isLoggedIn(apiEndpoint)); + assertEqualAsJson("{\"logged_in\":true,\"username\":\"" + username + "\",\"mediawiki_api_endpoint\":\"" + apiEndpoint + "\"}", + writer.toString()); } @Test @@ -125,25 +148,25 @@ public class LoginCommandTest extends CommandTest { whenNew(BasicApiConnection.class).withAnyArguments().thenReturn(connection); when(connection.getCurrentUser()).thenReturn(username); when(connection.getCookies()).thenReturn(makeResponseCookies()); - when(connection.getCookies()).thenReturn(makeResponseCookies()); when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken()); + when(request.getParameter(API_ENDPOINT)).thenReturn(apiEndpoint); when(request.getParameter(USERNAME)).thenReturn(username); when(request.getParameter(PASSWORD)).thenReturn(password); command.doPost(request, response); verify(connection).login(username, password); - assertTrue(ConnectionManager.getInstance().isLoggedIn()); - assertEqualAsJson("{\"logged_in\":true,\"username\":\"" + username + "\"}", writer.toString()); + + assertLogin(); Map cookies = getCookieMap(cookieCaptor.getAllValues()); assertEquals(cookies.size(), 5); - assertCookieEquals(cookies.get(WIKIBASE_USERNAME_COOKIE_KEY), "", 0); - assertCookieEquals(cookies.get(CONSUMER_TOKEN), "", 0); - assertCookieEquals(cookies.get(CONSUMER_SECRET), "", 0); - assertCookieEquals(cookies.get(ACCESS_TOKEN), "", 0); - assertCookieEquals(cookies.get(ACCESS_SECRET), "", 0); + assertCookieEquals(cookies.get(apiEndpointPrefix + USERNAME), "", 0); + assertCookieEquals(cookies.get(apiEndpointPrefix + CONSUMER_TOKEN), "", 0); + assertCookieEquals(cookies.get(apiEndpointPrefix + CONSUMER_SECRET), "", 0); + assertCookieEquals(cookies.get(apiEndpointPrefix + ACCESS_TOKEN), "", 0); + assertCookieEquals(cookies.get(apiEndpointPrefix + ACCESS_SECRET), "", 0); } @Test @@ -155,22 +178,22 @@ public class LoginCommandTest extends CommandTest { when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken()); when(request.getParameter("remember-credentials")).thenReturn("on"); + when(request.getParameter(API_ENDPOINT)).thenReturn(apiEndpoint); when(request.getParameter(USERNAME)).thenReturn(username); when(request.getParameter(PASSWORD)).thenReturn(password); command.doPost(request, response); verify(connection).login(username, password); - assertTrue(ConnectionManager.getInstance().isLoggedIn()); - assertEqualAsJson("{\"logged_in\":true,\"username\":\"" + username + "\"}", writer.toString()); + assertLogin(); Map cookies = getCookieMap(cookieCaptor.getAllValues()); - cookieMap.forEach((key, value) -> assertCookieEquals(cookies.get(WIKIDATA_COOKIE_PREFIX + key), value, ONE_YEAR)); - assertCookieEquals(cookies.get(WIKIBASE_USERNAME_COOKIE_KEY), username, ONE_YEAR); - assertCookieEquals(cookies.get(CONSUMER_TOKEN), "", 0); - assertCookieEquals(cookies.get(CONSUMER_SECRET), "", 0); - assertCookieEquals(cookies.get(ACCESS_TOKEN), "", 0); - assertCookieEquals(cookies.get(ACCESS_SECRET), "", 0); + cookieMap.forEach((key, value) -> assertCookieEquals(cookies.get(apiEndpointPrefix + WIKIBASE_COOKIE_PREFIX + key), value, ONE_YEAR)); + assertCookieEquals(cookies.get(apiEndpointPrefix + USERNAME), username, ONE_YEAR); + assertCookieEquals(cookies.get(apiEndpointPrefix + CONSUMER_TOKEN), "", 0); + assertCookieEquals(cookies.get(apiEndpointPrefix + CONSUMER_SECRET), "", 0); + assertCookieEquals(cookies.get(apiEndpointPrefix + ACCESS_TOKEN), "", 0); + assertCookieEquals(cookies.get(apiEndpointPrefix + ACCESS_SECRET), "", 0); } @Test @@ -181,20 +204,20 @@ public class LoginCommandTest extends CommandTest { when(connection.getCookies()).thenReturn(makeResponseCookies()); when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken()); + when(request.getParameter(API_ENDPOINT)).thenReturn(apiEndpoint); when(request.getCookies()).thenReturn(makeRequestCookies()); command.doPost(request, response); verify(connection).checkCredentials(); - assertTrue(ConnectionManager.getInstance().isLoggedIn()); - assertEqualAsJson("{\"logged_in\":true,\"username\":\"" + username + "\"}", writer.toString()); + assertLogin(); Map cookies = getCookieMap(cookieCaptor.getAllValues()); assertEquals(cookies.size(), 4); - assertCookieEquals(cookies.get(CONSUMER_TOKEN), "", 0); - assertCookieEquals(cookies.get(CONSUMER_SECRET), "", 0); - assertCookieEquals(cookies.get(ACCESS_TOKEN), "", 0); - assertCookieEquals(cookies.get(ACCESS_SECRET), "", 0); + assertCookieEquals(cookies.get(apiEndpointPrefix + CONSUMER_TOKEN), "", 0); + assertCookieEquals(cookies.get(apiEndpointPrefix + CONSUMER_SECRET), "", 0); + assertCookieEquals(cookies.get(apiEndpointPrefix + ACCESS_TOKEN), "", 0); + assertCookieEquals(cookies.get(apiEndpointPrefix + ACCESS_SECRET), "", 0); } @Test @@ -204,6 +227,7 @@ public class LoginCommandTest extends CommandTest { when(connection.getCurrentUser()).thenReturn(username); when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken()); + when(request.getParameter(API_ENDPOINT)).thenReturn(apiEndpoint); when(request.getParameter(CONSUMER_TOKEN)).thenReturn(consumerToken); when(request.getParameter(CONSUMER_SECRET)).thenReturn(consumerSecret); when(request.getParameter(ACCESS_TOKEN)).thenReturn(accessToken); @@ -211,16 +235,15 @@ public class LoginCommandTest extends CommandTest { command.doPost(request, response); - assertTrue(ConnectionManager.getInstance().isLoggedIn()); - assertEqualAsJson("{\"logged_in\":true,\"username\":\"" + username + "\"}", writer.toString()); + assertLogin(); Map cookies = getCookieMap(cookieCaptor.getAllValues()); assertEquals(cookies.size(), 5); - assertCookieEquals(cookies.get(WIKIBASE_USERNAME_COOKIE_KEY), "", 0); - assertCookieEquals(cookies.get(CONSUMER_TOKEN), "", 0); - assertCookieEquals(cookies.get(CONSUMER_SECRET), "", 0); - assertCookieEquals(cookies.get(ACCESS_TOKEN), "", 0); - assertCookieEquals(cookies.get(ACCESS_SECRET), "", 0); + assertCookieEquals(cookies.get(apiEndpointPrefix + USERNAME), "", 0); + assertCookieEquals(cookies.get(apiEndpointPrefix + CONSUMER_TOKEN), "", 0); + assertCookieEquals(cookies.get(apiEndpointPrefix + CONSUMER_SECRET), "", 0); + assertCookieEquals(cookies.get(apiEndpointPrefix + ACCESS_TOKEN), "", 0); + assertCookieEquals(cookies.get(apiEndpointPrefix + ACCESS_SECRET), "", 0); } @Test @@ -231,6 +254,7 @@ public class LoginCommandTest extends CommandTest { when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken()); when(request.getParameter("remember-credentials")).thenReturn("on"); + when(request.getParameter(API_ENDPOINT)).thenReturn(apiEndpoint); when(request.getParameter(CONSUMER_TOKEN)).thenReturn(consumerToken); when(request.getParameter(CONSUMER_SECRET)).thenReturn(consumerSecret); when(request.getParameter(ACCESS_TOKEN)).thenReturn(accessToken); @@ -239,37 +263,17 @@ public class LoginCommandTest extends CommandTest { command.doPost(request, response); - assertEqualAsJson("{\"logged_in\":true,\"username\":\"" + username + "\"}", writer.toString()); + assertLogin(); Map cookies = getCookieMap(cookieCaptor.getAllValues()); // If logging in with owner-only consumer, // cookies for the username/password login should be cleared. - cookieMap.forEach((key, value) -> assertCookieEquals(cookies.get(WIKIDATA_COOKIE_PREFIX + key), "", 0)); - assertCookieEquals(cookies.get(WIKIBASE_USERNAME_COOKIE_KEY), "", 0); - assertCookieEquals(cookies.get(CONSUMER_TOKEN), consumerToken, ONE_YEAR); - assertCookieEquals(cookies.get(CONSUMER_SECRET), consumerSecret, ONE_YEAR); - assertCookieEquals(cookies.get(ACCESS_TOKEN), accessToken, ONE_YEAR); - assertCookieEquals(cookies.get(ACCESS_SECRET), accessSecret, ONE_YEAR); - } - - @Test - public void testCookieEncoding() throws Exception { - OAuthApiConnection connection = mock(OAuthApiConnection.class); - whenNew(OAuthApiConnection.class).withAnyArguments().thenReturn(connection); - - when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken()); - when(request.getParameter("remember-credentials")).thenReturn("on"); - when(request.getParameter(CONSUMER_TOKEN)).thenReturn("malformed consumer token \r\n %?"); - when(request.getParameter(CONSUMER_SECRET)).thenReturn(consumerSecret); - when(request.getParameter(ACCESS_TOKEN)).thenReturn(accessToken); - when(request.getParameter(ACCESS_SECRET)).thenReturn(accessSecret); - when(request.getCookies()).thenReturn(makeRequestCookies()); - - command.doPost(request, response); - - Map cookies = getCookieMap(cookieCaptor.getAllValues()); - assertNotEquals(cookies.get(CONSUMER_TOKEN).getValue(), "malformed consumer token \r\n %?"); - assertEquals(cookies.get(CONSUMER_TOKEN).getValue(), "malformed+consumer+token+%0D%0A+%25%3F"); + cookieMap.forEach((key, value) -> assertCookieEquals(cookies.get(apiEndpointPrefix + WIKIBASE_COOKIE_PREFIX + key), "", 0)); + assertCookieEquals(cookies.get(apiEndpointPrefix + USERNAME), "", 0); + assertCookieEquals(cookies.get(apiEndpointPrefix + CONSUMER_TOKEN), consumerToken, ONE_YEAR); + assertCookieEquals(cookies.get(apiEndpointPrefix + CONSUMER_SECRET), consumerSecret, ONE_YEAR); + assertCookieEquals(cookies.get(apiEndpointPrefix + ACCESS_TOKEN), accessToken, ONE_YEAR); + assertCookieEquals(cookies.get(apiEndpointPrefix + ACCESS_SECRET), accessSecret, ONE_YEAR); } @Test @@ -279,23 +283,44 @@ public class LoginCommandTest extends CommandTest { when(connection.getCurrentUser()).thenReturn(username); when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken()); - Cookie consumerTokenCookie = new Cookie(CONSUMER_TOKEN, consumerToken); - Cookie consumerSecretCookie = new Cookie(CONSUMER_SECRET, consumerSecret); - Cookie accessTokenCookie = new Cookie(ACCESS_TOKEN, accessToken); - Cookie accessSecretCookie = new Cookie(ACCESS_SECRET, accessSecret); + when(request.getParameter(API_ENDPOINT)).thenReturn(apiEndpoint); + Cookie consumerTokenCookie = new Cookie(apiEndpointPrefix + CONSUMER_TOKEN, consumerToken); + Cookie consumerSecretCookie = new Cookie(apiEndpointPrefix + CONSUMER_SECRET, consumerSecret); + Cookie accessTokenCookie = new Cookie(apiEndpointPrefix + ACCESS_TOKEN, accessToken); + Cookie accessSecretCookie = new Cookie(apiEndpointPrefix + ACCESS_SECRET, accessSecret); when(request.getCookies()).thenReturn(new Cookie[]{consumerTokenCookie, consumerSecretCookie, accessTokenCookie, accessSecretCookie}); command.doPost(request, response); - assertTrue(ConnectionManager.getInstance().isLoggedIn()); - assertEqualAsJson("{\"logged_in\":true,\"username\":\"" + username + "\"}", writer.toString()); + assertLogin(); Map cookies = getCookieMap(cookieCaptor.getAllValues()); assertEquals(cookies.size(), 5); - assertCookieEquals(cookies.get(WIKIBASE_USERNAME_COOKIE_KEY), "", 0); - assertCookieEquals(cookies.get(CONSUMER_TOKEN), consumerToken, ONE_YEAR); - assertCookieEquals(cookies.get(CONSUMER_SECRET), consumerSecret, ONE_YEAR); - assertCookieEquals(cookies.get(ACCESS_TOKEN), accessToken, ONE_YEAR); - assertCookieEquals(cookies.get(ACCESS_SECRET), accessSecret, ONE_YEAR); + assertCookieEquals(cookies.get(apiEndpointPrefix + USERNAME), "", 0); + assertCookieEquals(cookies.get(apiEndpointPrefix + CONSUMER_TOKEN), consumerToken, ONE_YEAR); + assertCookieEquals(cookies.get(apiEndpointPrefix + CONSUMER_SECRET), consumerSecret, ONE_YEAR); + assertCookieEquals(cookies.get(apiEndpointPrefix + ACCESS_TOKEN), accessToken, ONE_YEAR); + assertCookieEquals(cookies.get(apiEndpointPrefix + ACCESS_SECRET), accessSecret, ONE_YEAR); + } + + @Test + public void testCookieEncoding() throws Exception { + OAuthApiConnection connection = mock(OAuthApiConnection.class); + whenNew(OAuthApiConnection.class).withAnyArguments().thenReturn(connection); + + when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken()); + when(request.getParameter("remember-credentials")).thenReturn("on"); + when(request.getParameter(API_ENDPOINT)).thenReturn(apiEndpoint); + when(request.getParameter(CONSUMER_TOKEN)).thenReturn("malformed consumer token \r\n %?"); + when(request.getParameter(CONSUMER_SECRET)).thenReturn(consumerSecret); + when(request.getParameter(ACCESS_TOKEN)).thenReturn(accessToken); + when(request.getParameter(ACCESS_SECRET)).thenReturn(accessSecret); + when(request.getCookies()).thenReturn(makeRequestCookies()); + + command.doPost(request, response); + + Map cookies = getCookieMap(cookieCaptor.getAllValues()); + assertNotEquals(cookies.get(apiEndpointPrefix + CONSUMER_TOKEN).getValue(), "malformed consumer token \r\n %?"); + assertEquals(cookies.get(apiEndpointPrefix + CONSUMER_TOKEN).getValue(), "malformed+consumer+token+%0D%0A+%25%3F"); } @Test @@ -308,15 +333,16 @@ public class LoginCommandTest extends CommandTest { when(connection.getCurrentUser()).thenReturn(username); when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken()); - Cookie consumerTokenCookie = new Cookie(CONSUMER_TOKEN, "malformed+consumer+token+%0D%0A+%25%3F"); - Cookie consumerSecretCookie = new Cookie(CONSUMER_SECRET, consumerSecret); - Cookie accessTokenCookie = new Cookie(ACCESS_TOKEN, accessToken); - Cookie accessSecretCookie = new Cookie(ACCESS_SECRET, accessSecret); + when(request.getParameter(API_ENDPOINT)).thenReturn(apiEndpoint); + Cookie consumerTokenCookie = new Cookie(apiEndpointPrefix + CONSUMER_TOKEN, "malformed+consumer+token+%0D%0A+%25%3F"); + Cookie consumerSecretCookie = new Cookie(apiEndpointPrefix + CONSUMER_SECRET, consumerSecret); + Cookie accessTokenCookie = new Cookie(apiEndpointPrefix + ACCESS_TOKEN, accessToken); + Cookie accessSecretCookie = new Cookie(apiEndpointPrefix + ACCESS_SECRET, accessSecret); when(request.getCookies()).thenReturn(new Cookie[]{consumerTokenCookie, consumerSecretCookie, accessTokenCookie, accessSecretCookie}); command.doPost(request, response); - verify(manager).login("malformed consumer token \r\n %?", consumerSecret, accessToken, accessSecret); + verify(manager).login(apiEndpoint, "malformed consumer token \r\n %?", consumerSecret, accessToken, accessSecret); } @Test @@ -327,6 +353,7 @@ public class LoginCommandTest extends CommandTest { when(connection.getCookies()).thenReturn(makeResponseCookies()); when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken()); + when(request.getParameter(API_ENDPOINT)).thenReturn(apiEndpoint); when(request.getParameter(USERNAME)).thenReturn(username); when(request.getParameter(PASSWORD)).thenReturn(password); @@ -335,8 +362,7 @@ public class LoginCommandTest extends CommandTest { int loginCookiesSize = cookieCaptor.getAllValues().size(); - assertTrue(ConnectionManager.getInstance().isLoggedIn()); - assertEqualAsJson("{\"logged_in\":true,\"username\":\"" + username + "\"}", writer.toString()); + assertLogin(); // logout when(request.getParameter("logout")).thenReturn("true"); @@ -346,16 +372,16 @@ public class LoginCommandTest extends CommandTest { command.doPost(request, response); - assertFalse(ConnectionManager.getInstance().isLoggedIn()); - assertEqualAsJson("{\"logged_in\":false,\"username\":null}", logoutWriter.toString()); + assertFalse(ConnectionManager.getInstance().isLoggedIn(apiEndpoint)); + assertEqualAsJson("{\"logged_in\":false,\"username\":null, \"mediawiki_api_endpoint\":\"" + apiEndpoint + "\"}", logoutWriter.toString()); Map cookies = getCookieMap(cookieCaptor.getAllValues().subList(loginCookiesSize, cookieCaptor.getAllValues().size())); - cookieMap.forEach((key, value) -> assertCookieEquals(cookies.get(WIKIDATA_COOKIE_PREFIX + key), "", 0)); - assertCookieEquals(cookies.get(WIKIBASE_USERNAME_COOKIE_KEY), "", 0); - assertCookieEquals(cookies.get(CONSUMER_TOKEN), "", 0); - assertCookieEquals(cookies.get(CONSUMER_SECRET), "", 0); - assertCookieEquals(cookies.get(ACCESS_TOKEN), "", 0); - assertCookieEquals(cookies.get(ACCESS_SECRET), "", 0); + cookieMap.forEach((key, value) -> assertCookieEquals(cookies.get(apiEndpointPrefix + WIKIBASE_COOKIE_PREFIX + key), "", 0)); + assertCookieEquals(cookies.get(apiEndpointPrefix + USERNAME), "", 0); + assertCookieEquals(cookies.get(apiEndpointPrefix + CONSUMER_TOKEN), "", 0); + assertCookieEquals(cookies.get(apiEndpointPrefix + CONSUMER_SECRET), "", 0); + assertCookieEquals(cookies.get(apiEndpointPrefix + ACCESS_TOKEN), "", 0); + assertCookieEquals(cookies.get(apiEndpointPrefix + ACCESS_SECRET), "", 0); } @Test @@ -365,6 +391,7 @@ public class LoginCommandTest extends CommandTest { doThrow(new LoginFailedException("login failed")).when(connection).login(username, password); when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken()); + when(request.getParameter(API_ENDPOINT)).thenReturn(apiEndpoint); // we don't check the username/password here when(request.getParameter(USERNAME)).thenReturn(username); when(request.getParameter(PASSWORD)).thenReturn(password); @@ -373,7 +400,7 @@ public class LoginCommandTest extends CommandTest { command.doPost(request, response); verify(connection).login(username, password); - assertFalse(ConnectionManager.getInstance().isLoggedIn()); + assertFalse(ConnectionManager.getInstance().isLoggedIn(apiEndpoint)); } @Test @@ -383,6 +410,7 @@ public class LoginCommandTest extends CommandTest { doThrow(new AssertUserFailedException("assert user login failed")).when(connection).checkCredentials(); when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken()); + when(request.getParameter(API_ENDPOINT)).thenReturn(apiEndpoint); // we don't check the username/password here when(request.getCookies()).thenReturn(makeRequestCookies()); @@ -390,7 +418,7 @@ public class LoginCommandTest extends CommandTest { command.doPost(request, response); verify(connection).checkCredentials(); - assertFalse(ConnectionManager.getInstance().isLoggedIn()); + assertFalse(ConnectionManager.getInstance().isLoggedIn(apiEndpoint)); } @Test @@ -400,6 +428,7 @@ public class LoginCommandTest extends CommandTest { doThrow(new AssertUserFailedException("assert user login failed")).when(connection).checkCredentials(); when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken()); + when(request.getParameter(API_ENDPOINT)).thenReturn(apiEndpoint); when(request.getParameter(CONSUMER_TOKEN)).thenReturn(consumerToken); when(request.getParameter(CONSUMER_SECRET)).thenReturn(consumerSecret); when(request.getParameter(ACCESS_TOKEN)).thenReturn(accessToken); @@ -418,13 +447,14 @@ public class LoginCommandTest extends CommandTest { when(connection.getCurrentUser()).thenReturn(username); when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken()); + when(request.getParameter(API_ENDPOINT)).thenReturn(apiEndpoint); when(request.getParameter(USERNAME)).thenReturn(username); when(request.getParameter(PASSWORD)).thenReturn(password); // login first command.doPost(request, response); - assertTrue(ConnectionManager.getInstance().isLoggedIn()); + assertTrue(ConnectionManager.getInstance().isLoggedIn(apiEndpoint)); // logout when(request.getParameter("logout")).thenReturn("true"); @@ -432,7 +462,7 @@ public class LoginCommandTest extends CommandTest { command.doPost(request, response); // still logged in - assertTrue(ConnectionManager.getInstance().isLoggedIn()); + assertTrue(ConnectionManager.getInstance().isLoggedIn(apiEndpoint)); } @Test @@ -445,13 +475,14 @@ public class LoginCommandTest extends CommandTest { when(connection.getCurrentUser()).thenReturn(username); when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken()); + when(request.getParameter(API_ENDPOINT)).thenReturn(apiEndpoint); when(request.getParameter(USERNAME)).thenReturn(username); when(request.getParameter(PASSWORD)).thenReturn(password); // login first command.doPost(request, response); - assertTrue(ConnectionManager.getInstance().isLoggedIn()); + assertTrue(ConnectionManager.getInstance().isLoggedIn(apiEndpoint)); // logout when(request.getParameter("logout")).thenReturn("true"); @@ -459,13 +490,53 @@ public class LoginCommandTest extends CommandTest { command.doPost(request, response); // not logged in anymore - assertFalse(ConnectionManager.getInstance().isLoggedIn()); + assertFalse(ConnectionManager.getInstance().isLoggedIn(apiEndpoint)); + } + + @Test + public void testMultipleConnections() throws Exception { + BasicApiConnection connection = mock(BasicApiConnection.class); + whenNew(BasicApiConnection.class).withAnyArguments().thenReturn(connection); + when(connection.getCurrentUser()).thenReturn(username); + + String wikibase1 = "https://www.wikibase1.org/w/api.php"; + String wikibase2 = "https://www.wikibase2.org/w/api.php"; + + when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken()); + when(request.getParameter(USERNAME)).thenReturn(username); + when(request.getParameter(PASSWORD)).thenReturn(password); + + // login to one endpoint first + when(request.getParameter(API_ENDPOINT)).thenReturn(wikibase1); + command.doPost(request, response); + + // not logged in to another endpoint + assertFalse(ConnectionManager.getInstance().isLoggedIn(wikibase2)); + + // login to another endpoint + when(request.getParameter(API_ENDPOINT)).thenReturn(wikibase2); + command.doPost(request, response); + + // logged in to both endpoints + assertTrue(ConnectionManager.getInstance().isLoggedIn(wikibase1)); + assertTrue(ConnectionManager.getInstance().isLoggedIn(wikibase2)); + + // logout from the first endpoint + when(request.getParameter("logout")).thenReturn("true"); + when(request.getParameter(API_ENDPOINT)).thenReturn(wikibase1); + command.doPost(request, response); + + // logged out from the first endpoint + assertFalse(ConnectionManager.getInstance().isLoggedIn(wikibase1)); + + // still logged in to another endpoint + assertTrue(ConnectionManager.getInstance().isLoggedIn(wikibase2)); } private static Cookie[] makeRequestCookies() { List cookies = new ArrayList<>(); - cookieMap.forEach((key, value) -> cookies.add(new Cookie(WIKIDATA_COOKIE_PREFIX + key, value))); - cookies.add(new Cookie(WIKIBASE_USERNAME_COOKIE_KEY, username)); + cookieMap.forEach((key, value) -> cookies.add(new Cookie(apiEndpointPrefix + WIKIBASE_COOKIE_PREFIX + key, value))); + cookies.add(new Cookie(apiEndpointPrefix + USERNAME, username)); return cookies.toArray(new Cookie[0]); } @@ -490,4 +561,10 @@ public class LoginCommandTest extends CommandTest { cookies.forEach(cookie -> map.put(cookie.getName(), cookie)); return map; } + + @Test + public void testRemoveCRLF() { + assertEquals(removeCRLF("a\rb\nc\r\n\r\nd"), "abcd"); + assertEquals(removeCRLF(null), ""); + } } diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/commands/PreviewWikibaseSchemaCommandTest.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/commands/PreviewWikibaseSchemaCommandTest.java index f449c9572..fe0ad5d87 100644 --- a/extensions/wikidata/tests/src/org/openrefine/wikidata/commands/PreviewWikibaseSchemaCommandTest.java +++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/commands/PreviewWikibaseSchemaCommandTest.java @@ -27,12 +27,8 @@ import static org.mockito.Mockito.when; import static org.openrefine.wikidata.testing.TestingData.jsonFromFile; import static org.testng.Assert.assertEquals; -import java.io.IOException; - -import javax.servlet.ServletException; - import org.openrefine.wikidata.qa.EditInspector; -import org.openrefine.wikidata.qa.WikidataConstraintFetcher; +import org.openrefine.wikidata.qa.ConstraintFetcher; import org.openrefine.wikidata.utils.EntityCacheStub; import org.powermock.api.mockito.PowerMockito; import org.powermock.core.classloader.annotations.PrepareForTest; @@ -43,6 +39,9 @@ import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.ObjectNode; import com.google.refine.util.ParsingUtilities; +import javax.servlet.ServletException; +import java.io.IOException; + @PrepareForTest(EditInspector.class) public class PreviewWikibaseSchemaCommandTest extends SchemaCommandTest { @@ -53,11 +52,13 @@ public class PreviewWikibaseSchemaCommandTest extends SchemaCommandTest { @Test public void testValidSchema() throws Exception { - WikidataConstraintFetcher fetcher = new WikidataConstraintFetcher(new EntityCacheStub()); - PowerMockito.whenNew(WikidataConstraintFetcher.class).withAnyArguments().thenReturn(fetcher); + ConstraintFetcher fetcher = new ConstraintFetcher(new EntityCacheStub(), "P2302"); + PowerMockito.whenNew(ConstraintFetcher.class).withAnyArguments().thenReturn(fetcher); - String schemaJson = jsonFromFile("schema/inception.json").toString(); + String schemaJson = jsonFromFile("schema/inception.json"); + String manifestJson = jsonFromFile("manifest/wikidata-manifest-v1.0.json"); when(request.getParameter("schema")).thenReturn(schemaJson); + when(request.getParameter("manifest")).thenReturn(manifestJson); command.doPost(request, response); @@ -66,4 +67,25 @@ public class PreviewWikibaseSchemaCommandTest extends SchemaCommandTest { assertEquals(3, edits.size()); } + @Test + public void testNoManifest() throws IOException, ServletException { + String schemaJson = jsonFromFile("schema/inception.json"); + when(request.getParameter("schema")).thenReturn(schemaJson); + + command.doPost(request, response); + + assertEquals(writer.toString(), "{\"code\":\"error\",\"message\":\"No Wikibase manifest provided.\"}"); + } + + @Test + public void testInvalidManifest() throws IOException, ServletException { + String schemaJson = jsonFromFile("schema/inception.json"); + String manifestJson = "{ invalid manifest"; + when(request.getParameter("schema")).thenReturn(schemaJson); + when(request.getParameter("manifest")).thenReturn(manifestJson); + + command.doPost(request, response); + + assertEquals(writer.toString(), "{\"code\":\"error\",\"message\":\"Wikibase manifest could not be parsed. Error message: invalid manifest format\"}"); + } } diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/commands/SaveWikibaseSchemaCommandTest.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/commands/SaveWikibaseSchemaCommandTest.java index 92e7c9913..14fa3ac90 100644 --- a/extensions/wikidata/tests/src/org/openrefine/wikidata/commands/SaveWikibaseSchemaCommandTest.java +++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/commands/SaveWikibaseSchemaCommandTest.java @@ -62,7 +62,7 @@ public class SaveWikibaseSchemaCommandTest extends SchemaCommandTest { when(request.getParameter("csrf_token")).thenReturn(Command.csrfFactory.getFreshToken()); String schemaJson = "{\"itemDocuments\":[{\"statementGroups\":[{\"statements\":[]}]," - +"\"nameDescs\":[]}],\"wikibasePrefix\":\"http://www.wikidata.org/entity/\"}"; + +"\"nameDescs\":[]}],\"siteIri\":\"http://www.wikidata.org/entity/\"}"; when(request.getParameter("schema")).thenReturn(schemaJson); command.doPost(request, response); diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/editing/EditBatchProcessorTest.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/editing/EditBatchProcessorTest.java index 519e1ecd0..8cd55a290 100644 --- a/extensions/wikidata/tests/src/org/openrefine/wikidata/editing/EditBatchProcessorTest.java +++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/editing/EditBatchProcessorTest.java @@ -52,8 +52,6 @@ import org.wikidata.wdtk.datamodel.interfaces.MonolingualTextValue; import org.wikidata.wdtk.wikibaseapi.WikibaseDataEditor; import org.wikidata.wdtk.wikibaseapi.WikibaseDataFetcher; import org.wikidata.wdtk.wikibaseapi.apierrors.MediaWikiApiErrorException; -import com.google.refine.ProjectManager; -import com.google.refine.preference.PreferenceStore; public class EditBatchProcessorTest extends WikidataRefineTest { @@ -61,6 +59,7 @@ public class EditBatchProcessorTest extends WikidataRefineTest { private WikibaseDataEditor editor = null; private NewItemLibrary library = null; private String summary = "my fantastic edits"; + private int maxlag = 5; private List tags = null; @BeforeMethod @@ -94,7 +93,7 @@ public class EditBatchProcessorTest extends WikidataRefineTest { .withLabel(label).withRevisionId(37828L).build(); when(editor.createItemDocument(expectedNewItem, summary, tags)).thenReturn(createdNewItem); - EditBatchProcessor processor = new EditBatchProcessor(fetcher, editor, batch, library, summary, tags, 50); + EditBatchProcessor processor = new EditBatchProcessor(fetcher, editor, batch, library, summary, maxlag, tags, 50); assertEquals(2, processor.remainingEdits()); assertEquals(0, processor.progress()); processor.performEdit(); @@ -139,7 +138,7 @@ public class EditBatchProcessorTest extends WikidataRefineTest { when(fetcher.getEntityDocuments(toQids(secondBatch))).thenReturn(toMap(secondBatch)); // Run edits - EditBatchProcessor processor = new EditBatchProcessor(fetcher, editor, batch, library, summary, tags, batchSize); + EditBatchProcessor processor = new EditBatchProcessor(fetcher, editor, batch, library, summary, maxlag, tags, batchSize); assertEquals(0, processor.progress()); for (int i = 124; i < 190; i++) { assertEquals(processor.remainingEdits(), 190 - i); @@ -159,18 +158,6 @@ public class EditBatchProcessorTest extends WikidataRefineTest { } } - @Test - public void testSetMaxLag() { - // use default value - EditBatchProcessor processor1 = new EditBatchProcessor(fetcher, editor, Collections.emptyList(), library, summary, tags, 50); - verify(editor, times(1)).setMaxLag(EditBatchProcessor.MAX_LAG_DEFAULT); - - // use value in preference store - ProjectManager.singleton.getPreferenceStore().put(EditBatchProcessor.MAX_LAG_KEY, "10"); - EditBatchProcessor processor2 = new EditBatchProcessor(fetcher, editor, Collections.emptyList(), library, summary, tags, 50); - verify(editor, times(1)).setMaxLag(10); - } - private Map toMap(List docs) { return docs.stream().collect(Collectors.toMap(doc -> doc.getEntityId().getId(), doc -> doc)); } diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/exporters/SchemaExporterTest.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/exporters/SchemaExporterTest.java index f5af95621..35a737d42 100644 --- a/extensions/wikidata/tests/src/org/openrefine/wikidata/exporters/SchemaExporterTest.java +++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/exporters/SchemaExporterTest.java @@ -23,7 +23,7 @@ public class SchemaExporterTest extends WikidataRefineTest { StringWriter writer = new StringWriter(); Properties properties = new Properties(); exporter.export(project, properties, engine, writer); - TestUtils.assertEqualAsJson("{\"itemDocuments\":[]}", writer.toString()); + TestUtils.assertEqualAsJson("{\"itemDocuments\":[],\"siteIri\":null,\"mediaWikiApiEndpoint\":null}", writer.toString()); } } diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/manifests/ManifestV1Test.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/manifests/ManifestV1Test.java new file mode 100644 index 000000000..d6858c2b2 --- /dev/null +++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/manifests/ManifestV1Test.java @@ -0,0 +1,75 @@ +package org.openrefine.wikidata.manifests; + +import org.openrefine.wikidata.testing.TestingData; +import org.testng.annotations.Test; + +import java.io.IOException; + +import static org.testng.AssertJUnit.assertEquals; + +public class ManifestV1Test { + + @Test + public void testGetters() throws IOException, ManifestException { + String json = TestingData.jsonFromFile("manifest/wikidata-manifest-v1.0.json"); + Manifest manifest = ManifestParser.parse(json); + assertEquals("1.0", manifest.getVersion()); + assertEquals("Wikidata", manifest.getName()); + assertEquals("https://www.wikidata.org/w/api.php", manifest.getMediaWikiApiEndpoint()); + assertEquals("http://www.wikidata.org/entity/", manifest.getSiteIri()); + assertEquals(5, manifest.getMaxlag()); + assertEquals("P31", manifest.getInstanceOfPid()); + assertEquals("P279", manifest.getSubclassOfPid()); + assertEquals("https://wdreconcile.toolforge.org/${lang}/api", manifest.getReconServiceEndpoint()); + assertEquals("P2302", manifest.getConstraintsRelatedId("property_constraint_pid")); + assertEquals("Q19474404", manifest.getConstraintsRelatedId("single_value_constraint_qid")); + } + + @Test + public void testInvalidManifestFormat() { + String invalidJson = "{invalid"; + try { + ManifestParser.parse(invalidJson); + } catch (ManifestException e) { + assertEquals("invalid manifest format", e.getMessage()); + } + } + + @Test + public void testMissingVersion() { + String missingVersion = "{\"a\": \"b\"}"; + try { + ManifestParser.parse(missingVersion); + } catch (ManifestException e) { + assertEquals("invalid manifest format, version is missing", e.getMessage()); + } + } + + @Test + public void testInvalidVersion() { + String invalidVersion1 = "{\"version\": \"a1.0\"}"; + try { + ManifestParser.parse(invalidVersion1); + } catch (ManifestException e) { + assertEquals("invalid version: a1.0", e.getMessage()); + } + + String invalidVersion2 = "{\"version\": \"1.1a\"}"; + try { + ManifestParser.parse(invalidVersion2); + } catch (ManifestException e) { + assertEquals("invalid version: 1.1a", e.getMessage()); + } + } + + @Test + public void testUnsupportedVersion() { + String unsupportedVersion = "{\"version\": \"2.0\"}"; + try { + ManifestParser.parse(unsupportedVersion); + } catch (ManifestException e) { + assertEquals("unsupported manifest version: 2.0", e.getMessage()); + } + } + +} diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/operations/PerformWikibaseEditsOperationTest.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/operations/PerformWikibaseEditsOperationTest.java index c66851fa7..2fca765bb 100644 --- a/extensions/wikidata/tests/src/org/openrefine/wikidata/operations/PerformWikibaseEditsOperationTest.java +++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/operations/PerformWikibaseEditsOperationTest.java @@ -58,7 +58,7 @@ public class PerformWikibaseEditsOperationTest extends OperationTest { @Test(expectedExceptions=IllegalArgumentException.class) public void testConstructor() { - new PerformWikibaseEditsOperation(EngineConfig.reconstruct("{}"), ""); + new PerformWikibaseEditsOperation(EngineConfig.reconstruct("{}"), "", 5); } @Test diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/ConstraintTest.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/ConstraintTest.java index e69a49681..365495886 100644 --- a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/ConstraintTest.java +++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/ConstraintTest.java @@ -23,10 +23,11 @@ import java.util.Set; import static org.openrefine.wikidata.qa.Constraint.CONSTRAINT_EXCEPTIONS; import static org.openrefine.wikidata.qa.Constraint.CONSTRAINT_STATUS; -import static org.openrefine.wikidata.qa.scrutinizers.SingleValueScrutinizer.SINGLE_VALUE_CONSTRAINT_QID; public class ConstraintTest { + public static final String SINGLE_VALUE_CONSTRAINT_QID = "Q19474404"; + public static ItemIdValue itemIdValue = Datamodel.makeWikidataItemIdValue(SINGLE_VALUE_CONSTRAINT_QID); public static PropertyIdValue constraintException = Datamodel.makeWikidataPropertyIdValue(CONSTRAINT_EXCEPTIONS); public static Value exceptionValue = Datamodel.makeWikidataItemIdValue("Q7409772"); diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/EditInspectorTest.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/EditInspectorTest.java new file mode 100644 index 000000000..e0e7fd4aa --- /dev/null +++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/EditInspectorTest.java @@ -0,0 +1,38 @@ +package org.openrefine.wikidata.qa; + +import org.openrefine.wikidata.manifests.Manifest; +import org.openrefine.wikidata.manifests.ManifestParser; +import org.openrefine.wikidata.testing.TestingData; +import org.testng.annotations.Test; + +import static org.testng.Assert.assertEquals; + +public class EditInspectorTest { + + private static final int scrutinizerCount = 22; + private static final int scrutinizerNotDependingOnPropertyConstraintCount = 7; + + @Test + public void testNoScrutinizerSkipped() throws Exception { + String manifestJson = TestingData.jsonFromFile("manifest/wikidata-manifest-v1.0.json"); + Manifest manifest = ManifestParser.parse(manifestJson); + EditInspector editInspector = new EditInspector(new QAWarningStore(), manifest); + assertEquals(editInspector.scrutinizers.size(), scrutinizerCount); + } + + @Test + public void toSkipScrutinizerDependingOnConstraintPropertyPid1() throws Exception { + String manifestJson = TestingData.jsonFromFile("manifest/wikidata-manifest-v1.0-without-constraints.json"); + Manifest manifest = ManifestParser.parse(manifestJson); + EditInspector editInspector = new EditInspector(new QAWarningStore(), manifest); + assertEquals(editInspector.scrutinizers.size(), scrutinizerNotDependingOnPropertyConstraintCount); + } + + @Test + public void toSkipScrutinizerDependingOnConstraintPropertyPid2() throws Exception { + String manifestJson = TestingData.jsonFromFile("manifest/wikidata-manifest-v1.0-missing-property-constraint-pid.json"); + Manifest manifest = ManifestParser.parse(manifestJson); + EditInspector editInspector = new EditInspector(new QAWarningStore(), manifest); + assertEquals(editInspector.scrutinizers.size(), scrutinizerNotDependingOnPropertyConstraintCount); + } +} diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/WikidataConstraintFetcherTests.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/WikidataConstraintFetcherTests.java deleted file mode 100644 index ad2986533..000000000 --- a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/WikidataConstraintFetcherTests.java +++ /dev/null @@ -1,62 +0,0 @@ -/******************************************************************************* - * MIT License - * - * Copyright (c) 2018 Antonin Delpeuch - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * 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 OR COPYRIGHT HOLDERS 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. - ******************************************************************************/ -package org.openrefine.wikidata.qa; - -import org.openrefine.wikidata.qa.scrutinizers.ConflictsWithScrutinizer; -import org.openrefine.wikidata.qa.scrutinizers.RestrictedValuesScrutinizer; -import org.openrefine.wikidata.utils.EntityCacheStub; -import org.testng.Assert; -import org.testng.annotations.Test; -import org.wikidata.wdtk.datamodel.helpers.Datamodel; -import org.wikidata.wdtk.datamodel.interfaces.PropertyIdValue; - -import java.util.ArrayList; - -public class WikidataConstraintFetcherTests { - - private ConstraintFetcher fetcher; - public static PropertyIdValue instanceOf; - - public WikidataConstraintFetcherTests() { - fetcher = new WikidataConstraintFetcher(new EntityCacheStub()); - instanceOf = Datamodel.makeWikidataPropertyIdValue("P31"); - } - - @Test - public void testGetConstraintsByType() { - Assert.assertEquals(fetcher.getConstraintsByType(instanceOf, ConflictsWithScrutinizer.CONFLICTS_WITH_CONSTRAINT_QID), new ArrayList<>()); - String constraintDefinitions = "[[ID P31$43E28495-355E-451E-A881-2EE14DFBE99D] http://www.wikidata.org/entity/P31 (property): http://www.wikidata.org/entity/P2302 :: http://www.wikidata.org/entity/Q52558054 (item)\n" + - " http://www.wikidata.org/entity/P2305 :: http://www.wikidata.org/entity/Q467 (item)\n" + - " http://www.wikidata.org/entity/P2305 :: http://www.wikidata.org/entity/Q6581072 (item)\n" + - " http://www.wikidata.org/entity/P2305 :: http://www.wikidata.org/entity/Q6581097 (item)\n" + - " http://www.wikidata.org/entity/P2305 :: http://www.wikidata.org/entity/Q8441 (item)\n" + - " http://www.wikidata.org/entity/P2305 :: http://www.wikidata.org/entity/Q171283 (item)\n" + - " http://www.wikidata.org/entity/P2305 :: http://www.wikidata.org/entity/Q11629 (item)\n" + - " http://www.wikidata.org/entity/P2305 :: http://www.wikidata.org/entity/Q11634 (item)\n" + - " http://www.wikidata.org/entity/P2305 :: http://www.wikidata.org/entity/Q131123 (item)\n" + - " http://www.wikidata.org/entity/P2316 :: http://www.wikidata.org/entity/Q21502408 (item)\n" + - "]"; - Assert.assertEquals(fetcher.getConstraintsByType(instanceOf, RestrictedValuesScrutinizer.DISALLOWED_VALUES_CONSTRAINT_QID).toString(), constraintDefinitions); - } -} diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/ConflictsWithScrutinizerTest.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/ConflictsWithScrutinizerTest.java index 2d4daf6c2..4e8b6b7b4 100644 --- a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/ConflictsWithScrutinizerTest.java +++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/ConflictsWithScrutinizerTest.java @@ -23,12 +23,13 @@ import java.util.List; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.openrefine.wikidata.qa.scrutinizers.ConflictsWithScrutinizer.CONFLICTS_WITH_CONSTRAINT_QID; -import static org.openrefine.wikidata.qa.scrutinizers.ConflictsWithScrutinizer.CONFLICTS_WITH_PROPERTY_PID; -import static org.openrefine.wikidata.qa.scrutinizers.ConflictsWithScrutinizer.ITEM_OF_PROPERTY_CONSTRAINT_PID; public class ConflictsWithScrutinizerTest extends ScrutinizerTest { + public static final String CONFLICTS_WITH_CONSTRAINT_QID = "Q21502838"; + public static final String CONFLICTS_WITH_PROPERTY_PID = "P2306"; + public static final String ITEM_OF_PROPERTY_CONSTRAINT_PID = "P2305"; + public static PropertyIdValue conflictsWithPid = Datamodel.makeWikidataPropertyIdValue("P2002"); public static Value conflictsWithValue = Datamodel.makeWikidataItemIdValue("Q36322"); public static PropertyIdValue propertyWithConflictsPid1 = Datamodel.makeWikidataPropertyIdValue("P31"); diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/DifferenceWithinScrutinizerTest.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/DifferenceWithinScrutinizerTest.java index 63f304554..4f60a0e1b 100644 --- a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/DifferenceWithinScrutinizerTest.java +++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/DifferenceWithinScrutinizerTest.java @@ -22,13 +22,14 @@ import java.util.List; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.openrefine.wikidata.qa.scrutinizers.DifferenceWithinRangeScrutinizer.DIFFERENCE_WITHIN_RANGE_CONSTRAINT_PID; -import static org.openrefine.wikidata.qa.scrutinizers.DifferenceWithinRangeScrutinizer.DIFFERENCE_WITHIN_RANGE_CONSTRAINT_QID; -import static org.openrefine.wikidata.qa.scrutinizers.DifferenceWithinRangeScrutinizer.MAXIMUM_VALUE_PID; -import static org.openrefine.wikidata.qa.scrutinizers.DifferenceWithinRangeScrutinizer.MINIMUM_VALUE_PID; public class DifferenceWithinScrutinizerTest extends ScrutinizerTest{ + public static String DIFFERENCE_WITHIN_RANGE_CONSTRAINT_QID = "Q21510854"; + public static String DIFFERENCE_WITHIN_RANGE_CONSTRAINT_PID = "P2306"; + public static String MINIMUM_VALUE_PID = "P2313"; + public static String MAXIMUM_VALUE_PID = "P2312"; + public static PropertyIdValue upperBoundPid = Datamodel.makeWikidataPropertyIdValue("P570"); public static PropertyIdValue lowerBoundPid = Datamodel.makeWikidataPropertyIdValue("P569"); public static QuantityValue minValue = Datamodel.makeQuantityValue(new BigDecimal(0)); diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/DistinctValuesScrutinizerTest.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/DistinctValuesScrutinizerTest.java index a368dad7e..a52a7c497 100644 --- a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/DistinctValuesScrutinizerTest.java +++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/DistinctValuesScrutinizerTest.java @@ -42,10 +42,11 @@ import java.util.List; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.openrefine.wikidata.qa.scrutinizers.DistinctValuesScrutinizer.DISTINCT_VALUES_CONSTRAINT_QID; public class DistinctValuesScrutinizerTest extends StatementScrutinizerTest { + public static String DISTINCT_VALUES_CONSTRAINT_QID = "Q21502410"; + public static PropertyIdValue propertyIdValue = Datamodel.makeWikidataPropertyIdValue("P163"); public static Value value1 = Datamodel.makeWikidataItemIdValue("Q41673"); public static Value value2 = Datamodel.makeWikidataItemIdValue("Q43175"); diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/EntityTypeScrutinizerTest.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/EntityTypeScrutinizerTest.java index d60aea79e..345a1fa8d 100644 --- a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/EntityTypeScrutinizerTest.java +++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/EntityTypeScrutinizerTest.java @@ -20,19 +20,20 @@ import java.util.List; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.openrefine.wikidata.qa.scrutinizers.EntityTypeScrutinizer.ALLOWED_ENTITY_TYPES_PID; -import static org.openrefine.wikidata.qa.scrutinizers.EntityTypeScrutinizer.ALLOWED_ENTITY_TYPES_QID; -import static org.openrefine.wikidata.qa.scrutinizers.EntityTypeScrutinizer.ALLOWED_ITEM_TYPE_QID; public class EntityTypeScrutinizerTest extends StatementScrutinizerTest { + public static String ALLOWED_ENTITY_TYPES_QID = "Q52004125"; + public static String ITEM_OF_PROPERTY_CONSTRAINT_PID = "P2305"; + public static String WIKIBASE_ITEM_QID = "Q29934200"; + public static PropertyIdValue propertyIdValue = Datamodel.makeWikidataPropertyIdValue("P2302"); public static Value propertyValue = Datamodel.makeWikidataItemIdValue("Q36322"); public static ItemIdValue entityIdValue = Datamodel.makeWikidataItemIdValue(ALLOWED_ENTITY_TYPES_QID); - public static PropertyIdValue itemParameterPID = Datamodel.makeWikidataPropertyIdValue(ALLOWED_ENTITY_TYPES_PID); + public static PropertyIdValue itemParameterPID = Datamodel.makeWikidataPropertyIdValue(ITEM_OF_PROPERTY_CONSTRAINT_PID); public static Value itemValue = Datamodel.makeWikidataItemIdValue("Q29934218"); - public static Value allowedValue = Datamodel.makeWikidataItemIdValue(ALLOWED_ITEM_TYPE_QID); + public static Value allowedValue = Datamodel.makeWikidataItemIdValue(WIKIBASE_ITEM_QID); diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/FormatScrutinizerTest.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/FormatScrutinizerTest.java index 40f3f505c..c13247fba 100644 --- a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/FormatScrutinizerTest.java +++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/FormatScrutinizerTest.java @@ -43,11 +43,12 @@ import java.util.List; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.openrefine.wikidata.qa.scrutinizers.FormatScrutinizer.FORMAT_CONSTRAINT_QID; -import static org.openrefine.wikidata.qa.scrutinizers.FormatScrutinizer.FORMAT_REGEX_PID; public class FormatScrutinizerTest extends ScrutinizerTest { + public static final String FORMAT_CONSTRAINT_QID = "Q21502404"; + public static final String FORMAT_REGEX_PID = "P1793"; + public static PropertyIdValue propertyIdValue = Datamodel.makeWikidataPropertyIdValue("P18"); public static Value completeMatchValue = Datamodel.makeStringValue("image.png"); public static Value noMatchValue = Datamodel.makeStringValue("image"); diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/InverseConstaintScrutinizerTest.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/InverseConstraintScrutinizerTest.java similarity index 94% rename from extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/InverseConstaintScrutinizerTest.java rename to extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/InverseConstraintScrutinizerTest.java index a041a33dc..0f46fc1c1 100644 --- a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/InverseConstaintScrutinizerTest.java +++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/InverseConstraintScrutinizerTest.java @@ -41,11 +41,12 @@ import java.util.List; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.openrefine.wikidata.qa.scrutinizers.InverseConstraintScrutinizer.INVERSE_CONSTRAINT_QID; -import static org.openrefine.wikidata.qa.scrutinizers.InverseConstraintScrutinizer.INVERSE_PROPERTY_PID; -import static org.openrefine.wikidata.qa.scrutinizers.InverseConstraintScrutinizer.SYMMETRIC_CONSTRAINT_QID; -public class InverseConstaintScrutinizerTest extends StatementScrutinizerTest { +public class InverseConstraintScrutinizerTest extends StatementScrutinizerTest { + + public static final String INVERSE_CONSTRAINT_QID = "Q21510855"; + public static final String SYMMETRIC_CONSTRAINT_QID = "Q21510862"; + public static final String INVERSE_PROPERTY_PID = "P2306"; public static PropertyIdValue propertyId = Datamodel.makeWikidataPropertyIdValue("P25"); public static ItemIdValue propertyValue = Datamodel.makeWikidataItemIdValue("Q345"); diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/ItemRequiresScrutinizerTest.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/ItemRequiresScrutinizerTest.java index c5172e642..014e00302 100644 --- a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/ItemRequiresScrutinizerTest.java +++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/ItemRequiresScrutinizerTest.java @@ -19,12 +19,13 @@ import java.util.List; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.openrefine.wikidata.qa.scrutinizers.ItemRequiresScrutinizer.ITEM_OF_PROPERTY_CONSTRAINT_PID; -import static org.openrefine.wikidata.qa.scrutinizers.ItemRequiresScrutinizer.ITEM_REQUIRES_CONSTRAINT_QID; -import static org.openrefine.wikidata.qa.scrutinizers.ItemRequiresScrutinizer.ITEM_REQUIRES_PROPERTY_PID; public class ItemRequiresScrutinizerTest extends ScrutinizerTest { + public static final String ITEM_REQUIRES_CONSTRAINT_QID = "Q21503247"; + public static final String ITEM_REQUIRES_PROPERTY_PID = "P2306"; + public static final String ITEM_OF_PROPERTY_CONSTRAINT_PID = "P2305"; + public static PropertyIdValue propertyIdValue = Datamodel.makeWikidataPropertyIdValue("P157"); public static ItemIdValue itemValue = Datamodel.makeWikidataItemIdValue("Q3187975"); public static ItemIdValue entityIdValue = Datamodel.makeWikidataItemIdValue(ITEM_REQUIRES_CONSTRAINT_QID); diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/MultiValueScrutinizerTest.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/MultiValueScrutinizerTest.java index 24d971984..4921580d3 100644 --- a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/MultiValueScrutinizerTest.java +++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/MultiValueScrutinizerTest.java @@ -18,10 +18,11 @@ import java.util.List; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.openrefine.wikidata.qa.scrutinizers.MultiValueScrutinizer.MULTI_VALUE_CONSTRAINT_QID; public class MultiValueScrutinizerTest extends ScrutinizerTest { + public static final String MULTI_VALUE_CONSTRAINT_QID = "Q21510857"; + public static PropertyIdValue propertyIdValue = Datamodel.makeWikidataPropertyIdValue("P1963"); public static Value valueSnak = Datamodel.makeWikidataItemIdValue("Q5"); public static ItemIdValue entityIdValue = Datamodel.makeWikidataItemIdValue(MULTI_VALUE_CONSTRAINT_QID); diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/QualifierCompatibilityScrutinizerTest.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/QualifierCompatibilityScrutinizerTest.java index 8288d8b68..4003c6c9e 100644 --- a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/QualifierCompatibilityScrutinizerTest.java +++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/QualifierCompatibilityScrutinizerTest.java @@ -44,12 +44,13 @@ import java.util.stream.Collectors; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.openrefine.wikidata.qa.scrutinizers.QualifierCompatibilityScrutinizer.ALLOWED_QUALIFIERS_CONSTRAINT_PID; -import static org.openrefine.wikidata.qa.scrutinizers.QualifierCompatibilityScrutinizer.ALLOWED_QUALIFIERS_CONSTRAINT_QID; -import static org.openrefine.wikidata.qa.scrutinizers.QualifierCompatibilityScrutinizer.MANDATORY_QUALIFIERS_CONSTRAINT_QID; public class QualifierCompatibilityScrutinizerTest extends StatementScrutinizerTest { + public static final String ALLOWED_QUALIFIERS_CONSTRAINT_QID = "Q21510851"; + public static final String MANDATORY_QUALIFIERS_CONSTRAINT_QID = "Q21510856"; + public static final String ALLOWED_QUALIFIERS_CONSTRAINT_PID = "P2306"; + public static ItemIdValue allowedQualifierEntity = Datamodel.makeWikidataItemIdValue(ALLOWED_QUALIFIERS_CONSTRAINT_QID); public static ItemIdValue mandatoryQualifierEntity = Datamodel.makeWikidataItemIdValue(MANDATORY_QUALIFIERS_CONSTRAINT_QID); public static PropertyIdValue propertyParameterPID = Datamodel.makeWikidataPropertyIdValue(ALLOWED_QUALIFIERS_CONSTRAINT_PID); diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/QuantityScrutinizerTest.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/QuantityScrutinizerTest.java index 1cf4caa22..8065cd09c 100644 --- a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/QuantityScrutinizerTest.java +++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/QuantityScrutinizerTest.java @@ -22,12 +22,13 @@ import java.util.List; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.openrefine.wikidata.qa.scrutinizers.QuantityScrutinizer.ALLOWED_UNITS_CONSTRAINT_PID; -import static org.openrefine.wikidata.qa.scrutinizers.QuantityScrutinizer.ALLOWED_UNITS_CONSTRAINT_QID; -import static org.openrefine.wikidata.qa.scrutinizers.QuantityScrutinizer.INTEGER_VALUED_CONSTRAINT_QID; -import static org.openrefine.wikidata.qa.scrutinizers.QuantityScrutinizer.NO_BOUNDS_CONSTRAINT_QID; public class QuantityScrutinizerTest extends ValueScrutinizerTest{ + + public static final String NO_BOUNDS_CONSTRAINT_QID = "Q51723761"; + public static final String INTEGER_VALUED_CONSTRAINT_QID = "Q52848401"; + public static final String ALLOWED_UNITS_CONSTRAINT_QID = "Q21514353"; + public static final String ALLOWED_UNITS_CONSTRAINT_PID = "P2305"; private QuantityValue exactValue = Datamodel.makeQuantityValue( new BigDecimal("1.234")); diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/RestrictedPositionScrutinizerTest.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/RestrictedPositionScrutinizerTest.java index 5c8117b14..02c074895 100644 --- a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/RestrictedPositionScrutinizerTest.java +++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/RestrictedPositionScrutinizerTest.java @@ -43,13 +43,14 @@ import java.util.List; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.openrefine.wikidata.qa.scrutinizers.RestrictedPositionScrutinizer.SCOPE_CONSTRAINT_PID; -import static org.openrefine.wikidata.qa.scrutinizers.RestrictedPositionScrutinizer.SCOPE_CONSTRAINT_QID; -import static org.openrefine.wikidata.qa.scrutinizers.RestrictedPositionScrutinizer.SCOPE_CONSTRAINT_QUALIFIER_QID; -import static org.openrefine.wikidata.qa.scrutinizers.RestrictedPositionScrutinizer.SCOPE_CONSTRAINT_VALUE_QID; public class RestrictedPositionScrutinizerTest extends SnakScrutinizerTest { + public static final String SCOPE_CONSTRAINT_QID = "Q53869507"; + public static final String SCOPE_CONSTRAINT_PID = "P5314"; + public static final String SCOPE_CONSTRAINT_VALUE_QID = "Q54828448"; + public static final String SCOPE_CONSTRAINT_QUALIFIER_QID = "Q54828449"; + public static PropertyIdValue propertyIdValue = Datamodel.makeWikidataPropertyIdValue("P22"); public static PropertyIdValue propertyScopeParameter = Datamodel.makeWikidataPropertyIdValue(SCOPE_CONSTRAINT_PID); public static ItemIdValue entityIdValue = Datamodel.makeWikidataItemIdValue(SCOPE_CONSTRAINT_QID); diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/RestrictedValuesScrutinizerTest.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/RestrictedValuesScrutinizerTest.java index 691dd5999..6aadd9539 100644 --- a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/RestrictedValuesScrutinizerTest.java +++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/RestrictedValuesScrutinizerTest.java @@ -18,11 +18,12 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.openrefine.wikidata.qa.scrutinizers.RestrictedValuesScrutinizer.ALLOWED_VALUES_CONSTRAINT_PID; -import static org.openrefine.wikidata.qa.scrutinizers.RestrictedValuesScrutinizer.ALLOWED_VALUES_CONSTRAINT_QID; -import static org.openrefine.wikidata.qa.scrutinizers.RestrictedValuesScrutinizer.DISALLOWED_VALUES_CONSTRAINT_QID; public class RestrictedValuesScrutinizerTest extends SnakScrutinizerTest { + + public static final String ALLOWED_VALUES_CONSTRAINT_QID = "Q21510859"; + public static final String DISALLOWED_VALUES_CONSTRAINT_QID = "Q52558054"; + public static final String ALLOWED_VALUES_CONSTRAINT_PID = "P2305"; private ItemIdValue qid = Datamodel.makeWikidataItemIdValue("Q3487"); public static PropertyIdValue allowedPropertyIdValue = Datamodel.makeWikidataPropertyIdValue("P1622"); diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/ScrutinizerTest.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/ScrutinizerTest.java index 3d3de0fe5..16359d1ad 100644 --- a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/ScrutinizerTest.java +++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/ScrutinizerTest.java @@ -23,9 +23,13 @@ ******************************************************************************/ package org.openrefine.wikidata.qa.scrutinizers; +import org.openrefine.wikidata.manifests.Manifest; +import org.openrefine.wikidata.manifests.ManifestException; +import org.openrefine.wikidata.manifests.ManifestParser; import org.openrefine.wikidata.qa.ConstraintFetcher; import org.openrefine.wikidata.qa.QAWarning; import org.openrefine.wikidata.qa.QAWarningStore; +import org.openrefine.wikidata.testing.TestingData; import org.openrefine.wikidata.updates.ItemUpdate; import org.testng.annotations.BeforeMethod; import org.wikidata.wdtk.datamodel.helpers.Datamodel; @@ -38,6 +42,7 @@ import org.wikidata.wdtk.datamodel.interfaces.SnakGroup; import org.wikidata.wdtk.datamodel.interfaces.Statement; import org.wikidata.wdtk.datamodel.interfaces.StatementRank; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -52,6 +57,17 @@ import static org.testng.Assert.assertTrue; public abstract class ScrutinizerTest { + private static Manifest manifest; + + static { + try { + String json = TestingData.jsonFromFile("manifest/wikidata-manifest-v1.0.json"); + manifest = ManifestParser.parse(json); + } catch (IOException | ManifestException e) { + e.printStackTrace(); + } + } + public abstract EditScrutinizer getScrutinizer(); private EditScrutinizer scrutinizer; @@ -62,6 +78,8 @@ public abstract class ScrutinizerTest { store = new QAWarningStore(); scrutinizer = getScrutinizer(); scrutinizer.setStore(store); + scrutinizer.setManifest(manifest); + scrutinizer.prepareDependencies(); } public void scrutinize(ItemUpdate... updates) { @@ -75,7 +93,7 @@ public abstract class ScrutinizerTest { } public void assertWarningsRaised(String... types) { - assertEquals(Arrays.asList(types).stream().collect(Collectors.toSet()), getWarningTypes()); + assertEquals(getWarningTypes(), Arrays.asList(types).stream().collect(Collectors.toSet())); } public void assertWarningRaised(QAWarning warning) { diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/SingleValueScrutinizerTest.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/SingleValueScrutinizerTest.java index 0778d90f4..b90e7b4a8 100644 --- a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/SingleValueScrutinizerTest.java +++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/SingleValueScrutinizerTest.java @@ -41,10 +41,11 @@ import java.util.List; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.openrefine.wikidata.qa.scrutinizers.SingleValueScrutinizer.SINGLE_VALUE_CONSTRAINT_QID; public class SingleValueScrutinizerTest extends ScrutinizerTest { + public static final String SINGLE_VALUE_CONSTRAINT_QID = "Q19474404"; + public static PropertyIdValue propertyIdValue = Datamodel.makeWikidataPropertyIdValue("P21"); public static Value value1 = Datamodel.makeWikidataItemIdValue("Q6581072"); public static Value value2 = Datamodel.makeWikidataItemIdValue("Q6581097"); diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/UnsourcedScrutinizerTest.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/UnsourcedScrutinizerTest.java index fae1f1ea9..f818fb821 100644 --- a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/UnsourcedScrutinizerTest.java +++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/UnsourcedScrutinizerTest.java @@ -43,10 +43,11 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.openrefine.wikidata.qa.scrutinizers.UnsourcedScrutinizer.CITATION_NEEDED_QID; public class UnsourcedScrutinizerTest extends StatementScrutinizerTest { + private static final String CITATION_NEEDED_QID = "Q54554025"; + public static PropertyIdValue propertyIdValue = Datamodel.makeWikidataPropertyIdValue("P2302"); public static PropertyIdValue referenceProperty = Datamodel.makeWikidataPropertyIdValue("P143"); public static ItemIdValue referenceValue = Datamodel.makeWikidataItemIdValue("Q348"); diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/UseAsQualifierScrutinizerTest.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/UseAsQualifierScrutinizerTest.java index 6202caa9f..49077c3f1 100644 --- a/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/UseAsQualifierScrutinizerTest.java +++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/qa/scrutinizers/UseAsQualifierScrutinizerTest.java @@ -19,12 +19,13 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import static org.openrefine.wikidata.qa.scrutinizers.UseAsQualifierScrutinizer.ITEM_OF_PROPERTY_CONSTRAINT_PID; -import static org.openrefine.wikidata.qa.scrutinizers.UseAsQualifierScrutinizer.ONE_OF_QUALIFIER_VALUE_PROPERTY_QID; -import static org.openrefine.wikidata.qa.scrutinizers.UseAsQualifierScrutinizer.QUALIFIER_PROPERTY_PID; public class UseAsQualifierScrutinizerTest extends ScrutinizerTest { + public static final String ONE_OF_QUALIFIER_VALUE_PROPERTY_QID = "Q52712340"; + public static final String QUALIFIER_PROPERTY_PID = "P2306"; + public static final String ITEM_OF_PROPERTY_CONSTRAINT_PID = "P2305"; + public static PropertyIdValue propertyIdValue = Datamodel.makeWikidataPropertyIdValue("P2302"); public static PropertyIdValue itemParameterPID = Datamodel.makeWikidataPropertyIdValue(ITEM_OF_PROPERTY_CONSTRAINT_PID); public static PropertyIdValue qualifierPID = Datamodel.makeWikidataPropertyIdValue(QUALIFIER_PROPERTY_PID); diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/schema/ExpressionContextTest.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/schema/ExpressionContextTest.java index c0f7b5f57..64f6f6ecc 100644 --- a/extensions/wikidata/tests/src/org/openrefine/wikidata/schema/ExpressionContextTest.java +++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/schema/ExpressionContextTest.java @@ -43,19 +43,19 @@ public class ExpressionContextTest extends WikidataRefineTest { @Test public void testGetCellByColumnName() { - ExpressionContext ctxt = new ExpressionContext("foo:", 1, project.rows.get(1), project.columnModel, null); + ExpressionContext ctxt = new ExpressionContext("foo:", "https://www.wikidata.org/w/api.php", 1, project.rows.get(1), project.columnModel, null); assertEquals("e", ctxt.getCellByName("b").value); } @Test public void testNonExistentColumn() { - ExpressionContext ctxt = new ExpressionContext("foo:", 1, project.rows.get(1), project.columnModel, null); + ExpressionContext ctxt = new ExpressionContext("foo:", "https://www.wikidata.org/w/api.php", 1, project.rows.get(1), project.columnModel, null); assertNull(ctxt.getCellByName("auie")); } @Test public void testGetRowId() { - ExpressionContext ctxt = new ExpressionContext("foo:", 1, project.rows.get(1), project.columnModel, null); + ExpressionContext ctxt = new ExpressionContext("foo:", "https://www.wikidata.org/w/api.php", 1, project.rows.get(1), project.columnModel, null); assertEquals(1, ctxt.getRowId()); } } diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/schema/WbExpressionTest.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/schema/WbExpressionTest.java index 7a84a19f9..ceceb8d2a 100644 --- a/extensions/wikidata/tests/src/org/openrefine/wikidata/schema/WbExpressionTest.java +++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/schema/WbExpressionTest.java @@ -26,11 +26,17 @@ package org.openrefine.wikidata.schema; import java.io.IOException; import java.io.Serializable; +import okhttp3.mockwebserver.Dispatcher; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import okhttp3.mockwebserver.RecordedRequest; import org.openrefine.wikidata.qa.QAWarningStore; import org.openrefine.wikidata.schema.exceptions.SkipSchemaExpressionException; import org.openrefine.wikidata.testing.TestingData; import org.openrefine.wikidata.testing.WikidataRefineTest; import org.testng.Assert; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; import org.testng.annotations.BeforeMethod; import com.google.refine.model.Cell; @@ -46,6 +52,28 @@ public class WbExpressionTest extends WikidataRefineTest { protected ExpressionContext ctxt; protected QAWarningStore warningStore; + protected static MockWebServer server; + + @BeforeClass + public void startServer() throws IOException { + server = new MockWebServer(); + String json = TestingData.jsonFromFile("langcode/wikidata-monolingualtext-langcode.json"); + server.setDispatcher(new Dispatcher() { + @Override + public MockResponse dispatch(RecordedRequest request) throws InterruptedException { + return new MockResponse() + .addHeader("Content-Type", "application/json; charset=utf-8") + .setBody(json); + } + }); + server.start(); + } + + @AfterClass + public void shutdownServer() throws IOException { + server.shutdown(); + } + @BeforeMethod public void createProject() throws IOException, ModelException { @@ -53,7 +81,7 @@ public class WbExpressionTest extends WikidataRefineTest { "column A,column B,column C,column D,column E\n" + "value A,value B,value C,value D,value E"); warningStore = new QAWarningStore(); row = project.rows.get(0); - ctxt = new ExpressionContext("http://www.wikidata.org/entity/", 0, row, project.columnModel, warningStore); + ctxt = new ExpressionContext("http://www.wikidata.org/entity/", server.url("/w/api.php").toString(), 0, row, project.columnModel, warningStore); } /** @@ -75,9 +103,7 @@ public class WbExpressionTest extends WikidataRefineTest { /** * Test that a particular expression is skipped. - * - * @param expected - * the expected evaluation of the value + * @param expression * the expression to evaluate */ @@ -107,7 +133,7 @@ public class WbExpressionTest extends WikidataRefineTest { row.cells.add(cell); } } - ctxt = new ExpressionContext("http://www.wikidata.org/entity/", 0, row, project.columnModel, warningStore); + ctxt = new ExpressionContext("http://www.wikidata.org/entity/", server.url("/w/api.php").toString(), 0, row, project.columnModel, warningStore); } /** diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/schema/WbLanguageConstantTest.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/schema/WbLanguageConstantTest.java index 07071c352..ef88c60c9 100644 --- a/extensions/wikidata/tests/src/org/openrefine/wikidata/schema/WbLanguageConstantTest.java +++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/schema/WbLanguageConstantTest.java @@ -54,4 +54,9 @@ public class WbLanguageConstantTest extends WbExpressionTest { assertNull(WbLanguageConstant.normalizeLanguageCode("non-existent language code")); assertNull(WbLanguageConstant.normalizeLanguageCode(null)); } + + @Test + public void testFallbackLangCodes() { + assertEquals("de", WbLanguageConstant.normalizeLanguageCode("de", "http://not.exist/w/api.php")); + } } diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/schema/WikibaseSchemaTest.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/schema/WikibaseSchemaTest.java index ce49c8fd4..ad3fde52a 100644 --- a/extensions/wikidata/tests/src/org/openrefine/wikidata/schema/WikibaseSchemaTest.java +++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/schema/WikibaseSchemaTest.java @@ -127,7 +127,7 @@ public class WikibaseSchemaTest extends WikidataRefineTest { @Test(expectedExceptions = IOException.class) public void testDeserializeEmpty() throws IOException { String schemaJson = "{\"itemDocuments\":[{\"statementGroups\":[{\"statements\":[]}]," - +"\"nameDescs\":[]}],\"wikibasePrefix\":\"http://www.wikidata.org/entity/\"}"; + +"\"nameDescs\":[]}],\"siteIri\":\"http://www.wikidata.org/entity/\"}"; WikibaseSchema.reconstruct(schemaJson); } diff --git a/extensions/wikidata/tests/src/org/openrefine/wikidata/utils/EntityCacheStub.java b/extensions/wikidata/tests/src/org/openrefine/wikidata/utils/EntityCacheStub.java index 8e010d512..ab6c4b36c 100644 --- a/extensions/wikidata/tests/src/org/openrefine/wikidata/utils/EntityCacheStub.java +++ b/extensions/wikidata/tests/src/org/openrefine/wikidata/utils/EntityCacheStub.java @@ -22,7 +22,7 @@ public class EntityCacheStub extends EntityCache { private ObjectMapper mapper = new DatamodelMapper(Datamodel.SITE_WIKIDATA); public EntityCacheStub() { - super(BasicApiConnection.getWikidataApiConnection()); + super(null, null); } @Override