Skip to content

Commit 4c45c88

Browse files
committed
Extract and show license key
List of available licenses is pre-extracted from UploadWizard MediaWiki extension. This should eventually be switched to use some live query to the site configuration. The license display is more or less localizable, for known templates. Unrecognized templates specified as parameters to {{self}} will be recognized (unlocalized) but others may not be. Change-Id: I9df5fe807798a191a3bb0a45464760c75f19e366
1 parent dde64dc commit 4c45c88

File tree

11 files changed

+440
-4
lines changed

11 files changed

+440
-4
lines changed

commons/res/layout/detail_main_panel.xml

+8
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,14 @@
3131
android:text="Description of the media goes here. This can potentially be fairly long, and will need to wrap across multiple lines. We hope it looks nice though."
3232
android:id="@+id/mediaDetailDesc"
3333
android:layout_gravity="left|start"/>
34+
<TextView
35+
android:layout_width="wrap_content"
36+
android:layout_height="wrap_content"
37+
android:text="License link"
38+
android:id="@+id/mediaDetailLicense"
39+
android:layout_gravity="left|start"
40+
android:textColor="@android:color/white"
41+
android:textSize="18sp" /> <!-- 18sp == MediumText -->
3442
<TextView
3543
android:layout_width="wrap_content"
3644
android:layout_height="wrap_content"

commons/res/values/strings.xml

+33
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,42 @@
9393
<string name="menu_download">Download</string>
9494
<string name="preference_license">License</string>
9595

96+
<!-- These three are semi-legacy entries, and should be changed in future -->
9697
<string name="license_name_cc_by_sa">CC\u00A0Attribution-ShareAlike\u00A03.0</string>
9798
<string name="license_name_cc_by">CC\u00A0Attribution\u00A03.0</string>
9899
<string name="license_name_cc0">CC0</string>
100+
101+
<!-- Licenses from the default UploadWizard selection list -->
102+
<string name="license_name_cc_by_sa_3_0">CC BY-SA 3.0</string>
103+
<string name="license_name_cc_by_sa_3_0_at">CC BY-SA 3.0 (Austria)</string>
104+
<string name="license_name_cc_by_sa_3_0_de">CC BY-SA 3.0 (Germany)</string>
105+
<string name="license_name_cc_by_sa_3_0_ee">CC BY-SA 3.0 (Estonia)</string>
106+
<string name="license_name_cc_by_sa_3_0_es">CC BY-SA 3.0 (Spain)</string>
107+
<string name="license_name_cc_by_sa_3_0_hr">CC BY-SA 3.0 (Croatia)</string>
108+
<string name="license_name_cc_by_sa_3_0_lu">CC BY-SA 3.0 (Luxembourg)</string>
109+
<string name="license_name_cc_by_sa_3_0_nl">CC BY-SA 3.0 (Netherlands)</string>
110+
<string name="license_name_cc_by_sa_3_0_no">CC BY-SA 3.0 (Norway)</string>
111+
<string name="license_name_cc_by_sa_3_0_pl">CC BY-SA 3.0 (Poland)</string>
112+
<string name="license_name_cc_by_sa_3_0_ro">CC BY-SA 3.0 (Romania)</string>
113+
<string name="license_name_cc_by_3_0">CC BY 3.0</string>
114+
<string name="license_name_cc_zero">CC Zero</string>
115+
<string name="license_name_own_pd">own-pd</string>
116+
<string name="license_name_cc_by_sa_2_5">CC BY-SA 2.5</string>
117+
<string name="license_name_cc_by_2_5">CC BY 2.5</string>
118+
<string name="license_name_cc_by_sa_2_0">CC BY-SA 2.0</string>
119+
<string name="license_name_cc_by_2_0">CC BY-SA 2.0</string>
120+
<string name="license_name_cc_2_0">CC BY 2.0</string>
121+
<string name="license_name_fal">Free Art License</string>
122+
<string name="license_name_pd_old_100">Public domain (author died over 100 years ago)</string>
123+
<string name="license_name_pd_old">Public domain (copyright expired)</string>
124+
<string name="license_name_pd_art">Public domain (art)</string>
125+
<string name="license_name_pd_us">Public domain (US)</string>
126+
<string name="license_name_pd_usgov">Public domain (US government)</string>
127+
<string name="license_name_pd_usgov_nasa">Public domain (NASA)</string>
128+
<string name="license_name_pd_ineligible">Public domain (ineligible for copyright)</string>
129+
<string name="license_name_attribution">Attribution</string>
130+
<string name="license_name_gfdl">GNU Free Documentation License</string>
131+
99132
<string name="welcome_wikipedia_text">Contribute your images. Help Wikipedia articles come to life!</string>
100133
<string name="welcome_wikipedia_subtext">Images on Wikipedia come from Wikimedia Commons.</string>
101134
<string name="welcome_copyright_text">Your images help educate people around the world.</string>
+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<licenses xmlns="https://www.mediawiki.org/wiki/Extension:UploadWizard/xmlns/licenses">
3+
<license id="cc-by-sa-3.0" template="cc-by-sa-3.0" url="https://creativecommons.org/licenses/by-sa/3.0/deed.$lang"/>
4+
<license id="cc-by-sa-3.0-at" template="cc-by-sa-3.0-at" url="https://creativecommons.org/licenses/by-sa/3.0/at/deed.$lang"/>
5+
<license id="cc-by-sa-3.0-de" template="cc-by-sa-3.0-de" url="https://creativecommons.org/licenses/by-sa/3.0/de/deed.$lang"/>
6+
<license id="cc-by-sa-3.0-ee" template="cc-by-sa-3.0-ee" url="https://creativecommons.org/licenses/by-sa/3.0/ee/deed.$lang"/>
7+
<license id="cc-by-sa-3.0-es" template="cc-by-sa-3.0-es" url="https://creativecommons.org/licenses/by-sa/3.0/es/deed.$lang"/>
8+
<license id="cc-by-sa-3.0-hr" template="cc-by-sa-3.0-hr" url="https://creativecommons.org/licenses/by-sa/3.0/hr/deed.$lang"/>
9+
<license id="cc-by-sa-3.0-lu" template="cc-by-sa-3.0-lu" url="https://creativecommons.org/licenses/by-sa/3.0/lu/deed.$lang"/>
10+
<license id="cc-by-sa-3.0-nl" template="cc-by-sa-3.0-nl" url="https://creativecommons.org/licenses/by-sa/3.0/nl/deed.$lang"/>
11+
<license id="cc-by-sa-3.0-no" template="cc-by-sa-3.0-no" url="https://creativecommons.org/licenses/by-sa/3.0/no/deed.$lang"/>
12+
<license id="cc-by-sa-3.0-pl" template="cc-by-sa-3.0-pl" url="https://creativecommons.org/licenses/by-sa/3.0/pl/deed.$lang"/>
13+
<license id="cc-by-sa-3.0-ro" template="cc-by-sa-3.0-ro" url="https://creativecommons.org/licenses/by-sa/3.0/ro/deed.$lang"/>
14+
<license id="cc-by-3.0" template="cc-by-3.0" url="https://creativecommons.org/licenses/by/3.0/deed.$lang"/>
15+
<license id="cc-zero" template="cc-zero" url="https://creativecommons.org/publicdomain/zero/1.0/deed.$lang"/>
16+
<license id="own-pd" template="cc-zero"/>
17+
<license id="cc-by-sa-2.5" template="cc-by-sa-2.5" url="https://creativecommons.org/licenses/by-sa/2.5/deed.$lang"/>
18+
<license id="cc-by-2.5" template="cc-by-2.5" url="https://creativecommons.org/licenses/by/2.5/deed.$lang"/>
19+
<license id="cc-by-sa-2.0" template="cc-by-sa-2.0" url="https://creativecommons.org/licenses/by-sa/2.0/deed.$lang"/>
20+
<license id="cc-by-2.0" template="cc-by-2.0" url="https://creativecommons.org/licenses/by/2.0/deed.$lang"/>
21+
<license id="fal" template="FAL"/>
22+
<license id="pd-old-100" template="PD-old-100"/>
23+
<license id="pd-old" template="PD-old"/>
24+
<license id="pd-art" template="PD-Art"/>
25+
<license id="pd-us" template="PD-US"/>
26+
<license id="pd-usgov" template="PD-USGov"/>
27+
<license id="pd-usgov-nasa" template="PD-USGov-NASA"/>
28+
<license id="pd-ineligible" template="pd-ineligible"/>
29+
<license id="attribution" template="attribution"/>
30+
<license id="gfdl" template="GFDL"/>
31+
</licenses>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package org.wikimedia.commons;
2+
3+
public class License {
4+
String key;
5+
String template;
6+
String url;
7+
String name;
8+
9+
public License(String key, String template, String url, String name) {
10+
if (key == null) {
11+
throw new RuntimeException("License.key must not be null");
12+
}
13+
if (template == null) {
14+
throw new RuntimeException("License.template must not be null");
15+
}
16+
this.key = key;
17+
this.template = template;
18+
this.url = url;
19+
this.name = name;
20+
}
21+
22+
public String getKey() {
23+
return key;
24+
}
25+
26+
public String getTemplate() {
27+
return template;
28+
}
29+
30+
public String getName() {
31+
if (name == null) {
32+
// hack
33+
return getKey();
34+
} else {
35+
return name;
36+
}
37+
}
38+
39+
public String getUrl(String language) {
40+
if (url == null) {
41+
return null;
42+
} else {
43+
return url.replace("$lang", language);
44+
}
45+
}
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package org.wikimedia.commons;
2+
3+
import android.app.Activity;
4+
import android.content.res.Resources;
5+
import android.util.Log;
6+
import org.xmlpull.v1.XmlPullParser;
7+
8+
import java.util.Collection;
9+
import java.util.HashMap;
10+
import java.util.Map;
11+
import java.util.Set;
12+
13+
public class LicenseList {
14+
Map<String, License> licenses = new HashMap<String, License>();
15+
Resources res;
16+
17+
private static String XMLNS_LICENSE = "https://www.mediawiki.org/wiki/Extension:UploadWizard/xmlns/licenses";
18+
19+
public LicenseList(Activity activity) {
20+
res = activity.getResources();
21+
XmlPullParser parser = res.getXml(R.xml.wikimedia_licenses);
22+
while (Utils.xmlFastForward(parser, XMLNS_LICENSE, "license")) {
23+
String id = parser.getAttributeValue(null, "id");
24+
String template = parser.getAttributeValue(null, "template");
25+
String url = parser.getAttributeValue(null, "url");
26+
String name = nameForTemplate(template);
27+
License license = new License(id, template, url, name);
28+
licenses.put(id, license);
29+
}
30+
31+
}
32+
33+
public Set<String> keySet() {
34+
return licenses.keySet();
35+
}
36+
37+
public Collection<License> values() {
38+
return licenses.values();
39+
}
40+
41+
public License get(String key) {
42+
return licenses.get(key);
43+
}
44+
45+
public License licenseForTemplate(String template) {
46+
String ucTemplate = Utils.capitalize(template);
47+
for (License license : values()) {
48+
if (ucTemplate.equals(Utils.capitalize(license.getTemplate()))) {
49+
return license;
50+
}
51+
}
52+
return null;
53+
}
54+
55+
public String nameIdForTemplate(String template) {
56+
// hack :D
57+
// cc-by-sa-3.0 -> cc_by_sa_3_0
58+
return "license_name_" + template.toLowerCase().replace("-", "_").replace(".", "_");
59+
}
60+
61+
private int stringIdByName(String stringId) {
62+
return res.getIdentifier("org.wikimedia.commons:string/" + stringId, null, null);
63+
}
64+
65+
public String nameForTemplate(String template) {
66+
Log.d("Commons", "LicenseList.nameForTemplate: template: " + template);
67+
String stringId = nameIdForTemplate(template);
68+
Log.d("Commons", "LicenseList.nameForTemplate: stringId: " + stringId);
69+
int nameId = stringIdByName(stringId);
70+
Log.d("Commons", "LicenseList.nameForTemplate: nameId: " + nameId);
71+
String name = res.getString(nameId);
72+
Log.d("Commons", "LicenseList.nameForTemplate: name: " + name);
73+
return name;
74+
}
75+
}

commons/src/main/java/org/wikimedia/commons/MediaDataExtractor.java

+46-2
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,19 @@ public class MediaDataExtractor {
3333
private Map<String, String> descriptions;
3434
private String author;
3535
private Date date;
36+
private String license;
37+
private LicenseList licenseList;
3638

3739
/**
3840
* @param filename of the target media object, should include 'File:' prefix
3941
*/
40-
public MediaDataExtractor(String filename) {
42+
public MediaDataExtractor(String filename, LicenseList licenseList) {
4143
this.filename = filename;
4244
categories = new ArrayList<String>();
4345
descriptions = new HashMap<String, String>();
4446
fetched = false;
4547
processed = false;
48+
this.licenseList = licenseList;
4649
}
4750

4851
/**
@@ -114,7 +117,41 @@ private void processWikiParseTree(String source) throws IOException {
114117
descriptions = getMultilingualText(descriptionNode);
115118

116119
Node authorNode = findTemplateParameter(templateNode, "author");
117-
author = Utils.getStringFromDOM(authorNode);
120+
author = getFlatText(authorNode);
121+
}
122+
123+
/*
124+
Pull up the license data list...
125+
look for the templates in two ways:
126+
* look for 'self' template and check its first parameter
127+
* if none, look for any of the known templates
128+
*/
129+
Log.d("Commons", "MediaDataExtractor searching for license");
130+
Node selfLicenseNode = findTemplate(doc.getDocumentElement(), "self");
131+
if (selfLicenseNode != null) {
132+
Node firstNode = findTemplateParameter(selfLicenseNode, 1);
133+
String licenseTemplate = getFlatText(firstNode);
134+
License license = licenseList.licenseForTemplate(licenseTemplate);
135+
if (license == null) {
136+
Log.d("Commons", "MediaDataExtractor found no matching license for self parameter: " + licenseTemplate + "; faking it");
137+
this.license = licenseTemplate; // hack hack! For non-selectable licenses that are still in the system.
138+
} else {
139+
// fixme: record the self-ness in here too... sigh
140+
// all this needs better server-side metadata
141+
this.license = license.getKey();
142+
Log.d("Commons", "MediaDataExtractor found self-license " + this.license);
143+
}
144+
} else {
145+
for (License license : licenseList.values()) {
146+
String templateName = license.getTemplate();
147+
Node template = findTemplate(doc.getDocumentElement(), templateName);
148+
if (template != null) {
149+
// Found!
150+
this.license = license.getKey();
151+
Log.d("Commons", "MediaDataExtractor found non-self license " + this.license);
152+
break;
153+
}
154+
}
118155
}
119156
}
120157

@@ -201,6 +238,10 @@ private Node findTemplateParameter(Node templateNode, TemplateChildNodeComparato
201238
throw new IOException("No matching template parameter node found.");
202239
}
203240

241+
private String getFlatText(Node parentNode) throws IOException {
242+
return parentNode.getTextContent();
243+
}
244+
204245
// Extract a dictionary of multilingual texts from a subset of the parse tree.
205246
// Texts are wrapped in things like {{en|foo} or {{en|1=foo bar}}.
206247
// Text outside those wrappers is stuffed into a 'default' faux language key if present.
@@ -246,6 +287,9 @@ public void fill(Media media) {
246287

247288
media.setCategories(categories);
248289
media.setDescriptions(descriptions);
290+
if (license != null) {
291+
media.setLicense(license);
292+
}
249293

250294
// add author, date, etc fields
251295
}

commons/src/main/java/org/wikimedia/commons/Utils.java

+31
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
import org.apache.commons.codec.digest.DigestUtils;
1111
import org.apache.commons.codec.net.URLCodec;
1212
import org.w3c.dom.Node;
13+
import org.xmlpull.v1.XmlPullParser;
14+
import org.xmlpull.v1.XmlPullParserException;
1315

1416
import javax.xml.transform.*;
1517
import java.io.*;
@@ -194,4 +196,33 @@ public static Uri uriForWikiPage(String name) {
194196
String uriStr = CommonsApplication.HOME_URL + urlEncode(underscored);
195197
return Uri.parse(uriStr);
196198
}
199+
200+
/**
201+
* Fast-forward an XmlPullParser to the next instance of the given element
202+
* in the input stream (namespaced).
203+
*
204+
* @param parser
205+
* @param namespace
206+
* @param element
207+
* @return true on match, false on failure
208+
*/
209+
public static boolean xmlFastForward(XmlPullParser parser, String namespace, String element) {
210+
try {
211+
while (parser.next() != XmlPullParser.END_DOCUMENT) {
212+
if (parser.getEventType() == XmlPullParser.START_TAG &&
213+
parser.getNamespace().equals(namespace) &&
214+
parser.getName().equals(element)) {
215+
// We found it!
216+
return true;
217+
}
218+
}
219+
return false;
220+
} catch (XmlPullParserException e) {
221+
e.printStackTrace();
222+
return false;
223+
} catch (IOException e) {
224+
e.printStackTrace();
225+
return false;
226+
}
227+
}
197228
}

commons/src/main/java/org/wikimedia/commons/media/MediaDetailFragment.java

+17-2
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ public static MediaDetailFragment forMedia(int index, boolean editable) {
5959

6060
private TextView title;
6161
private TextView desc;
62+
private TextView license;
6263
private ListView listView;
6364
private ArrayList<String> categoryNames;
6465
private boolean categoriesLoaded = false;
@@ -133,6 +134,7 @@ public void onItemClick(AdapterView<?> adapterView, View view, int position, lon
133134
spacer = (MediaDetailSpacer) detailView.findViewById(R.id.mediaDetailSpacer);
134135
title = (TextView) detailView.findViewById(R.id.mediaDetailTitle);
135136
desc = (TextView) detailView.findViewById(R.id.mediaDetailDesc);
137+
license = (TextView) detailView.findViewById(R.id.mediaDetailLicense);
136138

137139
// Enable or disable editing on the title
138140
/*
@@ -160,10 +162,12 @@ public void onItemClick(AdapterView<?> adapterView, View view, int position, lon
160162
// FIXME: cache this data
161163
detailFetchTask = new AsyncTask<Void, Void, Boolean>() {
162164
private MediaDataExtractor extractor;
165+
private LicenseList licenseList;
163166

164167
@Override
165168
protected void onPreExecute() {
166-
extractor = new MediaDataExtractor(media.getFilename());
169+
licenseList = new LicenseList(getActivity());
170+
extractor = new MediaDataExtractor(media.getFilename(), licenseList);
167171
}
168172

169173
@Override
@@ -187,6 +191,16 @@ protected void onPostExecute(Boolean success) {
187191
// Fill some fields
188192
desc.setText(media.getDescription("en"));
189193

194+
String licenseKey = media.getLicense();
195+
License licenseObj = licenseList.get(licenseKey);
196+
if (licenseObj == null) {
197+
license.setText(licenseKey);
198+
} else {
199+
license.setText(licenseObj.getName());
200+
}
201+
Log.d("Commons", "Media license is: " + media.getLicense());
202+
203+
190204
categoryNames.removeAll(categoryNames);
191205
categoryNames.addAll(media.getCategories());
192206

@@ -231,7 +245,8 @@ public void onLoadingCancelled(String s, View view) {
231245
}
232246

233247
title.setText(media.getDisplayTitle());
234-
desc.setText("");
248+
desc.setText(""); // fill in from network...
249+
license.setText(""); // fill in from network...
235250

236251
/*
237252
title.addTextChangedListener(new TextWatcher() {

0 commit comments

Comments
 (0)