commit 2e56e2f659c1254a80665ea9df0c62d139c0eadc
parent 4c94b05023567a2bfa1b1dae480ce87618d03b48
Author: Aurimas Vinckevicius <aurimas.dev@gmail.com>
Date: Thu, 6 Nov 2014 22:12:12 -0600
Add feed button and Add Feed dialog
Diffstat:
8 files changed, 280 insertions(+), 0 deletions(-)
diff --git a/chrome/content/zotero/feedSettings.js b/chrome/content/zotero/feedSettings.js
@@ -0,0 +1,187 @@
+/*
+ ***** BEGIN LICENSE BLOCK *****
+
+ Copyright © 2015 Center for History and New Media
+ George Mason University, Fairfax, Virginia, USA
+ http://zotero.org
+
+ This file is part of Zotero.
+
+ Zotero is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ Zotero is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with Zotero. If not, see <http://www.gnu.org/licenses/>.
+
+ ***** END LICENSE BLOCK *****
+*/
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Zotero_Feed_Settings
+//
+//////////////////////////////////////////////////////////////////////////////
+
+var Zotero_Feed_Settings = new function() {
+ let urlIsValid = true,
+ data = null,
+ feedReader = null,
+ urlTainted = false;
+
+ let cleanURL = function(url) {
+ url = url.trim();
+ if (!url) return;
+
+ let ios = Components.classes["@mozilla.org/network/io-service;1"]
+ .getService(Components.interfaces.nsIIOService);
+
+ let cleanUrl;
+ try {
+ let uri = ios.newURI(url, null, null);
+ if (uri.scheme != 'http' && uri.scheme != 'https') {
+ Zotero.debug(uri.scheme + " is not a supported protocol for feeds.");
+ }
+
+ cleanUrl = uri.spec;
+ } catch (e) {
+ if (e.result == Components.results.NS_ERROR_MALFORMED_URI) {
+ // Assume it's a URL missing "http://" part
+ try {
+ cleanUrl = ios.newURI('http://' + url, null, null).spec;
+ } catch (e) {}
+ }
+ throw e;
+ }
+
+ if (!cleanUrl) return;
+
+ if (/^https?:\/\/[^\/\s]+\/\S/.test(cleanUrl)) return cleanUrl;
+ };
+
+ this.init = function() {
+ this.toggleAdvancedOptions(false);
+
+ data = window.arguments[0];
+
+ if (data.url) {
+ document.getElementById('feed-url').value = data.url;
+ }
+
+ if (!data.url) {
+ this.invalidateUrl();
+ } else {
+ // Do not allow to change URL for existing feed
+ document.getElementById('feed-url').readOnly = true;
+ }
+
+ if (data.title) {
+ document.getElementById('feed-title').value = data.title;
+ }
+
+ let ttl;
+ if (data.ttl !== undefined) {
+ ttl = Math.floor(data.ttl / 60);
+ } else {
+ ttl = 1;
+ }
+ document.getElementById('feed-ttl').value = ttl;
+
+ let cleanAfter = data.cleanAfter;
+ if (cleanAfter === undefined) cleanAfter = 2;
+ document.getElementById('feed-cleanAfter').value = cleanAfter;
+
+ if (data.url && !data.urlIsValid) {
+ this.validateUrl();
+ }
+ };
+
+ this.invalidateUrl = function() {
+ urlTainted = true;
+ if (feedReader) {
+ feedReader.terminate();
+ feedReader = null;
+ }
+
+ if (!urlIsValid) return;
+
+ urlIsValid = false;
+ document.getElementById('feed-title').disabled = true;
+ document.getElementById('feed-ttl').disabled = true;
+ document.getElementById('feed-cleanAfter').disabled = true;
+ document.documentElement.getButton('accept').disabled = true;
+ };
+
+ this.validateUrl = function() {
+ if (feedReader) {
+ feedReader.terminate();
+ feedReader = null;
+ }
+
+ let url = cleanURL(document.getElementById('feed-url').value);
+ urlTainted = false;
+ if (!url) return;
+
+ let fr = feedReader = new Zotero.FeedReader(url);
+ fr.feedProperties
+ .then( feed => {
+ if (feedReader !== fr || urlTainted) return;
+
+ let title = document.getElementById('feed-title');
+ if (!data.url && feed.title) {
+ title.value = feed.title;
+ }
+
+ let ttl = document.getElementById('feed-ttl');
+ if (!data.url && feed.ttl) {
+ ttl.value = Math.floor(feed.ttl / 60) || 1;
+ }
+
+ document.getElementById('feed-url').value = url;
+
+ urlIsValid = true;
+ title.disabled = false;
+ ttl.disabled = false;
+ document.getElementById('feed-cleanAfter').disabled = false;
+ document.documentElement.getButton('accept').disabled = false;
+ })
+ .catch( e => {
+ Zotero.debug(e);
+ })
+ .finally( () => {
+ if (feedReader === fr) feedReader = null;
+ });
+ };
+
+ this.accept = function() {
+ data.url = document.getElementById('feed-url').value;
+ data.title = document.getElementById('feed-title').value;
+ data.ttl = document.getElementById('feed-ttl').value * 60;
+ data.cleanAfter = document.getElementById('feed-cleanAfter').value * 1;
+ return true;
+ };
+
+ this.cancel = function() {
+ data.cancelled = true;
+ return true;
+ };
+
+ /*
+ * Show/hide advanced options
+ * @param {Boolean} [show] If set, indicates whether the advanced
+ * options should be shown or not. If omitted, the options toggle
+ */
+ this.toggleAdvancedOptions = function(show) {
+ var opts = document.getElementById("advanced-options-togglable");
+ opts.hidden = show !== undefined ? !show : !opts.hidden;
+ document.getElementById("advanced-options")
+ .setAttribute("state", opts.hidden ? "closed" : "open");
+ window.sizeToContent();
+ };
+}
+\ No newline at end of file
diff --git a/chrome/content/zotero/feedSettings.xul b/chrome/content/zotero/feedSettings.xul
@@ -0,0 +1,50 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
+<?xml-stylesheet href="chrome://zotero/skin/zotero.css" type="text/css"?>
+<!DOCTYPE window [
+ <!ENTITY % zoteroDTD SYSTEM "chrome://zotero/locale/zotero.dtd" > %zoteroDTD;
+]>
+<dialog xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ title="&zotero.feedSettings.title;" buttons="cancel,accept"
+ buttonlabelaccept="&zotero.feedSettings.saveButton.label;"
+ ondialogaccept="Zotero_Feed_Settings.accept()"
+ ondialogcancel="Zotero_Feed_Settings.cancel()"
+ id="zotero-feed-settings"
+ onload="Zotero_Feed_Settings.init()">
+
+ <script src="include.js"/>
+ <script src="feedSettings.js"/>
+
+ <vbox>
+ <hbox align="center">
+ <label value="&zotero.feedSettings.url.label;" control="feed-url"/>
+ <textbox id="feed-url" flex="1" type="search" size="2"
+ oninput="Zotero_Feed_Settings.invalidateUrl()"
+ oncommand="Zotero_Feed_Settings.validateUrl()"
+ focused="true" newlines="replacewithspaces"
+ style="width: 30em; max-width: 30em"/>
+ </hbox>
+ <hbox align="center">
+ <label value="&zotero.feedSettings.title.label;" control="feed-url"/>
+ <textbox id="feed-title" flex="1" newlines="replacewithspaces"/>
+ </hbox>
+ <vbox id="advanced-options" class="zotero-advanced-options">
+ <hbox onclick="Zotero_Feed_Settings.toggleAdvancedOptions()" class="zotero-advanced-options-label">
+ <dropmarker/>
+ <label value="&zotero.general.advancedOptions.label;"/>
+ </hbox>
+ <vbox id="advanced-options-togglable">
+ <hbox align="center">
+ <label value="&zotero.feedSettings.refresh.label1;" control="feed-ttl"/>
+ <textbox id="feed-ttl" type="number" min="0" increment="1" size="3"/>
+ <label value="&zotero.feedSettings.refresh.label2;" control="feed-ttl"/>
+ </hbox>
+ <hbox align="center">
+ <label value="&zotero.feedSettings.cleanAfter.label1;" control="feed-cleanAfter"/>
+ <textbox id="feed-cleanAfter" type="number" min="0" increment="1" size="2"/>
+ <label value="&zotero.feedSettings.cleanAfter.label2;" control="feed-cleanAfter"/>
+ </hbox>
+ </vbox>
+ </vbox>
+ </vbox>
+</dialog>
+\ No newline at end of file
diff --git a/chrome/content/zotero/zoteroPane.js b/chrome/content/zotero/zoteroPane.js
@@ -839,6 +839,19 @@ var ZoteroPane = new function()
return collection.saveTx();
});
+ this.newFeed = function() {
+ let data = {};
+ window.openDialog('chrome://zotero/content/feedSettings.xul',
+ null, 'centerscreen, modal', data);
+ if (!data.cancelled) {
+ let feed = new Zotero.Feed();
+ feed.url = data.url;
+ feed.name = data.title;
+ feed.refreshInterval = data.ttl;
+ feed.cleanupAfter = data.cleanAfter;
+ feed.save({skipEditCheck: true});
+ }
+ }
this.newGroup = function () {
this.loadURI(Zotero.Groups.addGroupURL);
diff --git a/chrome/content/zotero/zoteroPane.xul b/chrome/content/zotero/zoteroPane.xul
@@ -58,6 +58,7 @@
<command id="cmd_zotero_createTimeline" oncommand="Zotero_Timeline_Interface.loadTimeline();"/>
<command id="cmd_zotero_rtfScan" oncommand="window.openDialog('chrome://zotero/content/rtfScan.xul', 'rtfScan', 'chrome,centerscreen')"/>
<command id="cmd_zotero_newCollection" oncommand="ZoteroPane_Local.newCollection()"/>
+ <command id="cmd_zotero_newFeed" oncommand="ZoteroPane_Local.newFeed()"/>
<command id="cmd_zotero_newSavedSearch" oncommand="ZoteroPane_Local.newSearch()"/>
<command id="cmd_zotero_newStandaloneNote" oncommand="ZoteroPane_Local.newNote(event.shiftKey);"/>
<command id="cmd_zotero_newChildNote" oncommand="ZoteroPane_Local.newChildNote(event.shiftKey);"/>
@@ -102,6 +103,7 @@
<toolbar id="zotero-toolbar" class="toolbar toolbar-primary">
<hbox id="zotero-collections-toolbar" align="center">
<toolbarbutton id="zotero-tb-collection-add" class="zotero-tb-button" tooltiptext="&zotero.toolbar.newCollection.label;" command="cmd_zotero_newCollection"/>
+ <toolbarbutton id="zotero-tb-feed-add" class="zotero-tb-button" tooltiptext="&zotero.toolbar.newFeed.label;" command="cmd_zotero_newFeed"/>
<toolbarbutton id="zotero-tb-group-add" class="zotero-tb-button" tooltiptext="&zotero.toolbar.newGroup;" oncommand="ZoteroPane_Local.newGroup()"/>
<spacer flex="1"/>
<toolbarbutton id="zotero-tb-actions-menu" class="zotero-tb-button" tooltiptext="&zotero.toolbar.actions.label;" type="menu">
diff --git a/chrome/locale/en-US/zotero/zotero.dtd b/chrome/locale/en-US/zotero/zotero.dtd
@@ -10,6 +10,7 @@
<!ENTITY zotero.general.cancel "Cancel">
<!ENTITY zotero.general.refresh "Refresh">
<!ENTITY zotero.general.saveAs "Save As…">
+<!ENTITY zotero.general.advancedOptions.label "Advanced Options">
<!ENTITY zotero.errorReport.title "Zotero Error Report">
<!ENTITY zotero.errorReport.unrelatedMessages "This may include messages unrelated to Zotero.">
@@ -103,6 +104,7 @@
<!ENTITY zotero.toolbar.lookup.label "Add Item(s) by Identifier">
<!ENTITY zotero.toolbar.removeItem.label "Remove Item…">
<!ENTITY zotero.toolbar.newCollection.label "New Collection…">
+<!ENTITY zotero.toolbar.newFeed.label "New Feed…">
<!ENTITY zotero.toolbar.newGroup "New Group…">
<!ENTITY zotero.toolbar.newSubcollection.label "New Subcollection…">
<!ENTITY zotero.toolbar.newSavedSearch.label "New Saved Search…">
@@ -254,6 +256,17 @@
<!ENTITY zotero.proxy.recognized.disable.label "Do not automatically redirect requests through previously recognized proxies">
<!ENTITY zotero.proxy.recognized.ignore.label "Ignore">
+<!ENTITY zotero.feedSettings.title "Feed Settings">
+<!ENTITY zotero.feedSettings.saveButton.label "Save">
+<!ENTITY zotero.feedSettings.url.label "URL">
+<!ENTITY zotero.feedSettings.title.label "Title">
+<!ENTITY zotero.feedSettings.refresh.label1 "Refresh Interval:">
+<!ENTITY zotero.feedSettings.refresh.label2 "hour(s)">
+<!ENTITY zotero.feedSettings.title.label "Title">
+<!ENTITY zotero.feedSettings.cleanAfter.label1 "Remove read articles after ">
+<!ENTITY zotero.feedSettings.cleanAfter.label2 "day(s)">
+
+
<!ENTITY zotero.recognizePDF.recognizing.label "Retrieving Metadata…">
<!ENTITY zotero.recognizePDF.cancel.label "Cancel">
<!ENTITY zotero.recognizePDF.pdfName.label "PDF Name">
diff --git a/chrome/skin/default/zotero/overlay.css b/chrome/skin/default/zotero/overlay.css
@@ -278,6 +278,11 @@
list-style-image: url('chrome://zotero/skin/toolbar-collection-add.png');
}
+#zotero-tb-feed-add
+{
+ list-style-image: url('chrome://zotero/skin/toolbar-feed-add.png');
+}
+
#zotero-tb-group-add
{
list-style-image: url('chrome://zotero/skin/group_add.png');
diff --git a/chrome/skin/default/zotero/toolbar-feed-add.png b/chrome/skin/default/zotero/toolbar-feed-add.png
Binary files differ.
diff --git a/chrome/skin/default/zotero/zotero.css b/chrome/skin/default/zotero/zotero.css
@@ -441,6 +441,14 @@ label.zotero-text-link {
max-width: 29.5em;
}
+.zotero-advanced-options>.zotero-advanced-options-label>dropmarker {
+ list-style-image: url("chrome://browser/skin/toolbarbutton-dropdown-arrow.png");
+ transform: rotate(270deg);
+}
+
+.zotero-advanced-options[state=open]>.zotero-advanced-options-label>dropmarker {
+ transform: none;
+}
/* BEGIN 2X BLOCK -- DO NOT EDIT MANUALLY -- USE 2XIZE */
@media (min-resolution: 1.5dppx) {