Various feeds changes
- Hide notes, tags and related for feed items in itembox - Add feed support for <enclosure> elements - Add feed syncing methods for synced settings (additional work is needed on the sync architecture to download synced settings from the server) - Change feed item clear policy to be less aggressive - Adjust for deasyncification - Disable translate-on-select - Close adomasven/zotero#7, Remove context menu items from feeds
This commit is contained in:
parent
0d4025e9fb
commit
12fc6cfbe8
42 changed files with 894 additions and 379 deletions
|
@ -341,7 +341,7 @@
|
|||
fieldNames.push(Zotero.ItemFields.getName(fields[i]));
|
||||
}
|
||||
|
||||
if (! (this.item instanceof Zotero.FeedItem)) {
|
||||
if (!(this.item instanceof Zotero.FeedItem)) {
|
||||
fieldNames.push("dateAdded", "dateModified");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,31 +36,28 @@ var Zotero_Feed_Settings = new function() {
|
|||
urlTainted = false;
|
||||
|
||||
let cleanURL = function(url) {
|
||||
let cleanUrl = Zotero.Utilities.cleanURL(url, true);
|
||||
let cleanURL = Zotero.Utilities.cleanURL(url, true);
|
||||
|
||||
if (cleanUrl) {
|
||||
if (/^https?:\/\/[^\/\s]+\/\S/.test(cleanUrl)) {
|
||||
return cleanUrl;
|
||||
if (cleanURL) {
|
||||
if (/^https?:\/\/[^\/\s]+\/\S/.test(cleanURL)) {
|
||||
return cleanURL;
|
||||
} else {
|
||||
Zotero.debug(uri.scheme + " is not a supported protocol for feeds.");
|
||||
Zotero.debug(uri.scheme + " is not a supported protocol for feeds");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.init = function() {
|
||||
this.init = Zotero.Promise.coroutine(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;
|
||||
} else {
|
||||
this.invalidateURL();
|
||||
}
|
||||
|
||||
if (data.title) {
|
||||
|
@ -71,20 +68,20 @@ var Zotero_Feed_Settings = new function() {
|
|||
if (data.ttl !== undefined) {
|
||||
ttl = Math.floor(data.ttl / 60);
|
||||
} else {
|
||||
ttl = 1;
|
||||
ttl = Zotero.Prefs.get('feeds.defaultTTL');
|
||||
}
|
||||
document.getElementById('feed-ttl').value = ttl;
|
||||
|
||||
let cleanupAfter = data.cleanupAfter;
|
||||
if (cleanupAfter === undefined) cleanupAfter = 2;
|
||||
if (cleanupAfter === undefined) cleanupAfter = Zotero.Prefs.get('feeds.defaultCleanupAfter');
|
||||
document.getElementById('feed-cleanupAfter').value = cleanupAfter;
|
||||
|
||||
if (data.url && !data.urlIsValid) {
|
||||
this.validateUrl();
|
||||
yield this.validateURL();
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
this.invalidateUrl = function() {
|
||||
this.invalidateURL = function() {
|
||||
urlTainted = true;
|
||||
if (feedReader) {
|
||||
feedReader.terminate();
|
||||
|
@ -100,7 +97,7 @@ var Zotero_Feed_Settings = new function() {
|
|||
document.documentElement.getButton('accept').disabled = true;
|
||||
};
|
||||
|
||||
this.validateUrl = Zotero.Promise.coroutine(function* () {
|
||||
this.validateURL = Zotero.Promise.coroutine(function* () {
|
||||
if (feedReader) {
|
||||
feedReader.terminate();
|
||||
feedReader = null;
|
||||
|
@ -111,11 +108,11 @@ var Zotero_Feed_Settings = new function() {
|
|||
if (!url) return;
|
||||
|
||||
try {
|
||||
let fr = feedReader = new Zotero.FeedReader(url);
|
||||
var fr = feedReader = new Zotero.FeedReader(url);
|
||||
yield fr.process();
|
||||
let feed = fr.feedProperties;
|
||||
// Prevent progress if textbox changes triggered another call to
|
||||
// validateUrl / invalidateUrl (old session)
|
||||
// validateURL / invalidateURL (old session)
|
||||
if (feedReader !== fr || urlTainted) return;
|
||||
|
||||
let title = document.getElementById('feed-title');
|
||||
|
|
|
@ -15,36 +15,48 @@
|
|||
<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/>
|
||||
<grid>
|
||||
<columns>
|
||||
<column/>
|
||||
<column flex="1"/>
|
||||
</columns>
|
||||
|
||||
<rows>
|
||||
<row>
|
||||
<hbox align="center">
|
||||
<label value="&zotero.feedSettings.url.label;" control="feed-url"/>
|
||||
</hbox>
|
||||
<textbox id="feed-url" flex="1" size="2"
|
||||
oninput="Zotero_Feed_Settings.invalidateURL();Zotero_Feed_Settings.validateURL()"
|
||||
focused="true" newlines="stripsurroundingwhitespace"
|
||||
style="width: 30em; max-width: 30em"/>
|
||||
</row>
|
||||
<row>
|
||||
<hbox align="center">
|
||||
<label value="&zotero.feedSettings.title.label;" control="feed-url"/>
|
||||
</hbox>
|
||||
<textbox id="feed-title" flex="1" newlines="replacewithspaces"/>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
<vbox id="advanced-options" class="zotero-advanced-options">
|
||||
<hbox onclick="Zotero_Feed_Settings.toggleAdvancedOptions()" class="zotero-advanced-options-label">
|
||||
<dropmarker/>
|
||||
<hbox align="center">
|
||||
<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.cleanupAfter.label1;" control="feed-cleanupAfter"/>
|
||||
<textbox id="feed-cleanupAfter" type="number" min="0" increment="1" size="2"/>
|
||||
<label value="&zotero.feedSettings.cleanupAfter.label2;" control="feed-cleanupAfter"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</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.cleanupAfter.label1;" control="feed-cleanupAfter"/>
|
||||
<textbox id="feed-cleanupAfter" type="number" min="0" increment="1" size="2"/>
|
||||
<label value="&zotero.feedSettings.cleanupAfter.label2;" control="feed-cleanupAfter"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
</vbox>
|
||||
</dialog>
|
|
@ -86,6 +86,10 @@ var ZoteroItemPane = new function() {
|
|||
|
||||
_lastItem = item;
|
||||
|
||||
// Hide for feed items
|
||||
document.getElementById('zotero-editpane-tabs').setAttribute('hidden', item.isFeedItem);
|
||||
document.getElementById('zotero-view-item').classList.add('no-tabs');
|
||||
|
||||
if (index == 1) {
|
||||
var editable = ZoteroPane_Local.canEdit();
|
||||
_notesButton.hidden = !editable;
|
||||
|
|
|
@ -52,41 +52,41 @@
|
|||
|
||||
<!-- Regular item -->
|
||||
<tabbox id="zotero-view-tabbox" flex="1" onselect="if (!ZoteroPane_Local.collectionsView.selection || event.originalTarget.localName != 'tabpanels') { return; }; ZoteroItemPane.viewItem(ZoteroPane_Local.getSelectedItems()[0], ZoteroPane_Local.collectionsView.editable ? 'edit' : 'view', this.selectedIndex)">
|
||||
<tabs>
|
||||
<tab label="&zotero.tabs.info.label;"/>
|
||||
<tab label="&zotero.tabs.notes.label;"/>
|
||||
<tab label="&zotero.tabs.tags.label;"/>
|
||||
<tab label="&zotero.tabs.related.label;"/>
|
||||
<tabs id="zotero-editpane-tabs">
|
||||
<tab id="zotero-editpane-info-tab" label="&zotero.tabs.info.label;"/>
|
||||
<tab id="zotero-editpane-notes-tab" label="&zotero.tabs.notes.label;"/>
|
||||
<tab id="zotero-editpane-tags-tab" label="&zotero.tabs.tags.label;"/>
|
||||
<tab id="zotero-editpane-related-tab" label="&zotero.tabs.related.label;"/>
|
||||
</tabs>
|
||||
<tabpanels id="zotero-view-item" flex="1">
|
||||
<tabpanel>
|
||||
<zoteroitembox id="zotero-editpane-item-box" flex="1"/>
|
||||
</tabpanel>
|
||||
|
||||
<tabpanel flex="1" orient="vertical">
|
||||
<vbox flex="1" class="zotero-box">
|
||||
<hbox align="center">
|
||||
<label id="zotero-editpane-notes-label"/>
|
||||
<button id="zotero-editpane-notes-add" label="&zotero.item.add;" oncommand="ZoteroItemPane.addNote(event.shiftKey);"/>
|
||||
</hbox>
|
||||
<grid flex="1">
|
||||
<columns>
|
||||
<column flex="1"/>
|
||||
<column/>
|
||||
</columns>
|
||||
<rows id="zotero-editpane-dynamic-notes" flex="1"/>
|
||||
</grid>
|
||||
</vbox>
|
||||
</tabpanel>
|
||||
|
||||
<tabpanel>
|
||||
<tagsbox id="zotero-editpane-tags" flex="1"/>
|
||||
</tabpanel>
|
||||
|
||||
<tabpanel>
|
||||
<relatedbox id="zotero-editpane-related" flex="1"/>
|
||||
</tabpanel>
|
||||
</tabpanels>
|
||||
<tabpanels id="zotero-view-item" flex="1">
|
||||
<tabpanel>
|
||||
<zoteroitembox id="zotero-editpane-item-box" flex="1"/>
|
||||
</tabpanel>
|
||||
|
||||
<tabpanel flex="1" orient="vertical">
|
||||
<vbox flex="1" class="zotero-box">
|
||||
<hbox align="center">
|
||||
<label id="zotero-editpane-notes-label"/>
|
||||
<button id="zotero-editpane-notes-add" label="&zotero.item.add;" oncommand="ZoteroItemPane.addNote(event.shiftKey);"/>
|
||||
</hbox>
|
||||
<grid flex="1">
|
||||
<columns>
|
||||
<column flex="1"/>
|
||||
<column/>
|
||||
</columns>
|
||||
<rows id="zotero-editpane-dynamic-notes" flex="1"/>
|
||||
</grid>
|
||||
</vbox>
|
||||
</tabpanel>
|
||||
|
||||
<tabpanel>
|
||||
<tagsbox id="zotero-editpane-tags" flex="1"/>
|
||||
</tabpanel>
|
||||
|
||||
<tabpanel>
|
||||
<relatedbox id="zotero-editpane-related" flex="1"/>
|
||||
</tabpanel>
|
||||
</tabpanels>
|
||||
</tabbox>
|
||||
|
||||
<!-- Note item -->
|
||||
|
|
|
@ -816,7 +816,7 @@ Zotero_Preferences.Keys = {
|
|||
for (var i=0; i<rows.length; i++) {
|
||||
// Display the appropriate modifier keys for the platform
|
||||
let label = rows[i].firstChild.nextSibling;
|
||||
if (label.className == 'cmd-shift') {
|
||||
if (label.className == 'modifier') {
|
||||
label.value = Zotero.isMac ? Zotero.getString('general.keys.cmdShift') : Zotero.getString('general.keys.ctrlShift');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,12 @@
|
|||
|
||||
***** END LICENSE BLOCK *****
|
||||
-->
|
||||
<!DOCTYPE prefwindow SYSTEM "chrome://zotero/locale/preferences.dtd">
|
||||
<!DOCTYPE window [
|
||||
<!ENTITY % zoteroDTD SYSTEM "chrome://zotero/locale/zotero.dtd">
|
||||
%zoteroDTD;
|
||||
<!ENTITY % preferencesDTD SYSTEM "chrome://zotero/locale/preferences.dtd">
|
||||
%preferencesDTD;
|
||||
]>
|
||||
|
||||
<overlay id="zotero-prefpane-advanced-overlay"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
@ -50,13 +55,18 @@
|
|||
<preference id="pref-keys-importFromClipboard" name="extensions.zotero.keys.importFromClipboard" type="string"/>
|
||||
<preference id="pref-keys-copySelectedItemCitationsToClipboard" name="extensions.zotero.keys.copySelectedItemCitationsToClipboard" type="string"/>
|
||||
<preference id="pref-keys-copySelectedItemsToClipboard" name="extensions.zotero.keys.copySelectedItemsToClipboard" type="string"/>
|
||||
|
||||
<preference id="pref-feeds-sortAscending" name="extensions.zotero.feeds.sortAscending" type="bool"/>
|
||||
<preference id="pref-feeds-defaultTTL" name="extensions.zotero.feeds.defaultTTL" type="int"/>
|
||||
<preference id="pref-feeds-defaultCleanupAfter" name="extensions.zotero.feeds.defaultCleanupAfter" type="int"/>
|
||||
</preferences>
|
||||
|
||||
<tabbox id="zotero-prefpane-advanced-tabs">
|
||||
<tabs>
|
||||
<tab label="&zotero.preferences.prefpane.general;"/>
|
||||
<tab label="&zotero.preferences.advanced.filesAndFolders;"/>
|
||||
<tab label="&zotero.preferences.advanced.keys;"/>
|
||||
<tab label="&zotero.preferences.advanced.keys;"/>
|
||||
<tab label="&zotero.preferences.feeds;"/>
|
||||
</tabs>
|
||||
|
||||
<tabpanels id="zotero-prefpane-advanced-tabpanels">
|
||||
|
@ -218,37 +228,37 @@
|
|||
<rows id="zotero-keys-rows">
|
||||
<row id="zotero-keys-new-item">
|
||||
<label value="&zotero.preferences.keys.newItem;" control="textbox-newItem"/>
|
||||
<label class="cmd-shift"/>
|
||||
<label class="modifier"/>
|
||||
<textbox id="textbox-newItem" maxlength="1" size="1" preference="pref-keys-newItem"/>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<label value="&zotero.preferences.keys.newNote;" control="textbox-newNote"/>
|
||||
<label class="cmd-shift"/>
|
||||
<label class="modifier"/>
|
||||
<textbox id="textbox-newNote" maxlength="1" size="1" preference="pref-keys-newNote"/>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<label value="&zotero.preferences.keys.importFromClipboard;" control="textbox-importFromClipboard"/>
|
||||
<label class="cmd-shift"/>
|
||||
<label class="modifier"/>
|
||||
<textbox id="textbox-importFromClipboard" maxlength="1" size="1" preference="pref-keys-importFromClipboard"/>
|
||||
</row>
|
||||
|
||||
<row id="zotero-keys-focus-libraries-pane">
|
||||
<label value="&zotero.preferences.keys.focusLibrariesPane;" control="textbox-library"/>
|
||||
<label class="cmd-shift"/>
|
||||
<label class="modifier"/>
|
||||
<textbox id="textbox-library" maxlength="1" size="1" preference="pref-keys-library"/>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<label value="&zotero.preferences.keys.quicksearch;" control="textbox-quicksearch"/>
|
||||
<label class="cmd-shift"/>
|
||||
<label class="modifier"/>
|
||||
<textbox id="textbox-quicksearch" maxlength="1" size="1" preference="pref-keys-quicksearch"/>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<label value="&zotero.preferences.keys.copySelectedItemCitationsToClipboard;" control="textbox-copySelectedItemCitationsToClipboard"/>
|
||||
<label class="cmd-shift"/>
|
||||
<label class="modifier"/>
|
||||
<textbox id="textbox-copySelectedItemCitationsToClipboard" maxlength="1" size="1"
|
||||
preference="pref-keys-copySelectedItemCitationsToClipboard"
|
||||
onchange="if (Zotero_Preferences.Export) { Zotero_Preferences.Export.updateQuickCopyInstructions(); }"/>
|
||||
|
@ -256,7 +266,7 @@
|
|||
|
||||
<row>
|
||||
<label value="&zotero.preferences.keys.copySelectedItemsToClipboard;" control="textbox-copySelectedItemsToClipboard"/>
|
||||
<label class="cmd-shift"/>
|
||||
<label class="modifier"/>
|
||||
<textbox id="textbox-copySelectedItemsToClipboard" maxlength="1" size="1"
|
||||
preference="pref-keys-copySelectedItemsToClipboard"
|
||||
onchange="if (Zotero_Preferences.Export) { Zotero_Preferences.Export.updateQuickCopyInstructions(); }"/>
|
||||
|
@ -264,7 +274,7 @@
|
|||
|
||||
<row>
|
||||
<label value="&zotero.preferences.keys.toggleTagSelector;" control="textbox-toggleTagSelector"/>
|
||||
<label class="cmd-shift"/>
|
||||
<label class="modifier"/>
|
||||
<textbox id="textbox-toggleTagSelector" maxlength="1" size="1" preference="pref-keys-toggleTagSelector"/>
|
||||
</row>
|
||||
|
||||
|
@ -280,6 +290,40 @@
|
|||
|
||||
<separator/>
|
||||
</tabpanel>
|
||||
<tabpanel id="zotero-prefpane-advanced-feeds-tab" orient="vertical">
|
||||
<groupbox>
|
||||
<hbox>
|
||||
<hbox align="center">
|
||||
<label value="&zotero.preferences.feeds.sorting.label1;"/>
|
||||
<menulist id="feed-sort" preference="pref-feeds-sortAscending">
|
||||
<menupopup>
|
||||
<menuitem label="&zotero.preferences.feeds.sorting.newest;" value="false"/>
|
||||
<menuitem label="&zotero.preferences.feeds.sorting.oldest;" value="true"/>
|
||||
</menupopup>
|
||||
</menulist>
|
||||
<label value="&zotero.preferences.feeds.sorting.label2;"/>
|
||||
</hbox>
|
||||
</hbox>
|
||||
</groupbox>
|
||||
|
||||
<groupbox>
|
||||
<caption label="&zotero.preferences.feeds.feedDefaults;"/>
|
||||
<hbox>
|
||||
<hbox align="center">
|
||||
<label value="&zotero.feedSettings.refresh.label1;"/>
|
||||
<textbox maxlength="2" size="2" preference="pref-feeds-defaultTTL"/>
|
||||
<label value="&zotero.feedSettings.refresh.label2;"/>
|
||||
</hbox>
|
||||
</hbox>
|
||||
<hbox>
|
||||
<hbox align="center">
|
||||
<label value="&zotero.feedSettings.cleanupAfter.label1;"/>
|
||||
<textbox maxlength="3" size="3" preference="pref-feeds-defaultCleanupAfter"/>
|
||||
<label value="&zotero.feedSettings.cleanupAfter.label2;"/>
|
||||
</hbox>
|
||||
</hbox>
|
||||
</groupbox>
|
||||
</tabpanel>
|
||||
</tabpanels>
|
||||
</tabbox>
|
||||
|
||||
|
|
|
@ -51,19 +51,19 @@
|
|||
<rows id="zotero-keys-rows">
|
||||
<row insertbefore="zotero-keys-new-item">
|
||||
<label value="&zotero.preferences.keys.openZotero;" control="key-textbox-openZotero"/>
|
||||
<label class="cmd-shift"/>
|
||||
<label class="modifier"/>
|
||||
<textbox id="textbox-openZotero" maxlength="1" size="1" preference="pref-keys-openZotero"/>
|
||||
</row>
|
||||
|
||||
<row insertbefore="zotero-keys-new-item">
|
||||
<label value="&zotero.preferences.keys.toggleFullscreen;" control="textbox-toggleFullscreen"/>
|
||||
<label class="cmd-shift"/>
|
||||
<label class="modifier"/>
|
||||
<textbox id="textbox-toggleFullscreen" maxlength="1" size="1" preference="pref-keys-toggleFullscreen"/>
|
||||
</row>
|
||||
|
||||
<row insertbefore="zotero-keys-new-item">
|
||||
<label value="&zotero.preferences.keys.saveToZotero;" control="key-textbox-saveToZotero"/>
|
||||
<label class="cmd-shift"/>
|
||||
<label class="modifier"/>
|
||||
<textbox id="textbox-saveToZotero" maxlength="1" size="1" preference="pref-keys-saveToZotero"/>
|
||||
</row>
|
||||
</rows>
|
||||
|
|
|
@ -44,7 +44,6 @@
|
|||
<preference id="pref-groups-copyChildLinks" name="extensions.zotero.groups.copyChildLinks" type="bool"/>
|
||||
<preference id="pref-groups-copyTags" name="extensions.zotero.groups.copyTags" type="bool"/>
|
||||
|
||||
<preference id="pref-feeds-sortAsc" name="extensions.zotero.feeds.sortAsc" type="bool"/>
|
||||
</preferences>
|
||||
|
||||
<groupbox id="zotero-prefpane-general-groupbox">
|
||||
|
@ -132,18 +131,6 @@
|
|||
</vbox>
|
||||
</groupbox>
|
||||
|
||||
<groupbox>
|
||||
<caption label="&zotero.preferences.feeds;"/>
|
||||
|
||||
<label value="&zotero.preferences.feeds.sorting;"/>
|
||||
<vbox style="margin-left: 2em">
|
||||
<radiogroup id="feed-sort" orient="horizontal" align="center" preference="pref-feeds-sortAsc">
|
||||
<radio label="&zotero.preferences.feeds.sorting.newestFirst;" value="false"/>
|
||||
<radio label="&zotero.preferences.feeds.sorting.oldestFirst;" value="true"/>
|
||||
</radiogroup>
|
||||
</vbox>
|
||||
</groupbox>
|
||||
|
||||
<separator/>
|
||||
<separator/>
|
||||
</prefpane>
|
||||
|
|
|
@ -689,6 +689,7 @@ Zotero.Attachments = new function(){
|
|||
* @deprecated Use Zotero.Utilities.cleanURL instead
|
||||
*/
|
||||
this.cleanAttachmentURI = function (uri, tryHttp) {
|
||||
Zotero.debug("Zotero.Attachments.cleanAttachmentURI() is deprecated -- use Zotero.Utilities.cleanURL");
|
||||
return Zotero.Utilities.cleanURL(uri, tryHttp);
|
||||
}
|
||||
|
||||
|
|
|
@ -152,13 +152,13 @@ Zotero.CollectionTreeRow.prototype.__defineGetter__('editable', function () {
|
|||
if (this.isTrash() || this.isShare() || this.isBucket()) {
|
||||
return false;
|
||||
}
|
||||
if (this.isGroup() || this.isFeed()) {
|
||||
return this.ref.editable;
|
||||
}
|
||||
if (!this.isWithinGroup() || this.isPublications()) {
|
||||
return true;
|
||||
}
|
||||
var libraryID = this.ref.libraryID;
|
||||
if (this.isGroup() || this.isFeed()) {
|
||||
return this.ref.editable;
|
||||
}
|
||||
if (this.isCollection() || this.isSearch() || this.isDuplicates() || this.isUnfiled()) {
|
||||
var type = Zotero.Libraries.get(libraryID).libraryType;
|
||||
if (type == 'group') {
|
||||
|
|
|
@ -724,11 +724,14 @@ Zotero.CollectionTreeView.prototype.getImageSrc = function(row, col)
|
|||
switch (collectionType) {
|
||||
case 'library':
|
||||
case 'feed':
|
||||
// Better alternative needed: https://github.com/zotero/zotero/pull/902#issuecomment-183185973
|
||||
/*
|
||||
if (treeRow.ref.updating) {
|
||||
collectionType += '-updating';
|
||||
} else if (treeRow.ref.lastCheckError) {
|
||||
collectionType += '-error';
|
||||
}
|
||||
*/
|
||||
break;
|
||||
|
||||
case 'trash':
|
||||
|
@ -1337,7 +1340,7 @@ Zotero.CollectionTreeView.prototype._rememberOpenStates = Zotero.Promise.corouti
|
|||
var open = this.isContainerOpen(i);
|
||||
|
||||
// Collections and feeds default to closed
|
||||
if (!open && treeRow.isCollection() || treeRow.isFeed()) {
|
||||
if ((!open && treeRow.isCollection()) || treeRow.isFeed()) {
|
||||
delete state[treeRow.id];
|
||||
continue;
|
||||
}
|
||||
|
@ -1938,7 +1941,7 @@ Zotero.CollectionTreeView.prototype.drop = Zotero.Promise.coroutine(function* (r
|
|||
}
|
||||
|
||||
var items = yield Zotero.Items.getAsync(ids);
|
||||
if (!items) {
|
||||
if (items.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -757,11 +757,16 @@ Zotero.DataObject.prototype.isEditable = function () {
|
|||
|
||||
|
||||
Zotero.DataObject.prototype.editCheck = function () {
|
||||
let library = Zotero.Libraries.get(this.libraryID);
|
||||
if ((this._objectType == 'collection' || this._objectType == 'search')
|
||||
&& Zotero.Libraries.get(this.libraryID).libraryType == 'publications') {
|
||||
&& library.libraryType == 'publications') {
|
||||
throw new Error(this._ObjectTypePlural + " cannot be added to My Publications");
|
||||
}
|
||||
|
||||
if (library.libraryType == 'feed') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.isEditable()) {
|
||||
throw new Error("Cannot edit " + this._objectType + " in read-only library "
|
||||
+ Zotero.Libraries.getName(this.libraryID));
|
||||
|
|
|
@ -70,6 +70,8 @@ Zotero.Feed = function(params = {}) {
|
|||
this._feedUnreadCount = null;
|
||||
|
||||
this._updating = false;
|
||||
this._syncedSettings = null;
|
||||
this._previousURL = null;
|
||||
}
|
||||
|
||||
Zotero.Feed._colToProp = function(c) {
|
||||
|
@ -79,13 +81,13 @@ Zotero.Feed._colToProp = function(c) {
|
|||
Zotero.extendClass(Zotero.Library, Zotero.Feed);
|
||||
|
||||
Zotero.defineProperty(Zotero.Feed, '_unreadCountSQL', {
|
||||
value: "(SELECT COUNT(*) FROM items I JOIN feedItems FeI USING (itemID)"
|
||||
+ " WHERE I.libraryID=F.libraryID AND FeI.readTime IS NULL) AS _feedUnreadCount"
|
||||
value: "(SELECT COUNT(*) FROM items I JOIN feedItems FI USING (itemID)"
|
||||
+ " WHERE I.libraryID=F.libraryID AND FI.readTime IS NULL) AS _feedUnreadCount"
|
||||
});
|
||||
|
||||
Zotero.defineProperty(Zotero.Feed, '_dbColumns', {
|
||||
value: Object.freeze(['name', 'url', 'lastUpdate', 'lastCheck',
|
||||
'lastCheckError', 'lastGUID', 'cleanupAfter', 'refreshInterval'])
|
||||
'lastCheckError', 'cleanupAfter', 'refreshInterval'])
|
||||
});
|
||||
|
||||
Zotero.defineProperty(Zotero.Feed, '_primaryDataSQLParts');
|
||||
|
@ -130,7 +132,7 @@ for (let i=0; i<accessors.length; i++) {
|
|||
set: function(v) this._set(prop, v)
|
||||
})
|
||||
}
|
||||
let getters = ['lastCheck', 'lastUpdate', 'lastCheckError', 'lastGUID'];
|
||||
let getters = ['lastCheck', 'lastUpdate', 'lastCheckError'];
|
||||
for (let i=0; i<getters.length; i++) {
|
||||
let name = getters[i];
|
||||
let prop = Zotero.Feed._colToProp(name);
|
||||
|
@ -179,6 +181,7 @@ Zotero.Feed.prototype._set = function (prop, val) {
|
|||
if (uri.scheme !== 'http' && uri.scheme !== 'https') {
|
||||
throw new Error(invalidUrlError);
|
||||
}
|
||||
this._previousURL = this.url;
|
||||
break;
|
||||
case '_feedRefreshInterval':
|
||||
case '_feedCleanupAfter':
|
||||
|
@ -212,7 +215,6 @@ Zotero.Feed.prototype._loadDataFromRow = function(row) {
|
|||
this._feedLastCheckError = row._feedLastCheckError || null;
|
||||
this._feedLastCheck = row._feedLastCheck || null;
|
||||
this._feedLastUpdate = row._feedLastUpdate || null;
|
||||
this._feedLastGUID = row._feedLastGUID || null;
|
||||
this._feedCleanupAfter = parseInt(row._feedCleanupAfter) || null;
|
||||
this._feedRefreshInterval = parseInt(row._feedRefreshInterval) || null;
|
||||
this._feedUnreadCount = parseInt(row._feedUnreadCount);
|
||||
|
@ -225,7 +227,7 @@ Zotero.Feed.prototype._reloadFromDB = Zotero.Promise.coroutine(function* () {
|
|||
});
|
||||
|
||||
Zotero.defineProperty(Zotero.Feed.prototype, '_childObjectTypes', {
|
||||
value: Object.freeze(['feedItem'])
|
||||
value: Object.freeze(['feedItem', 'item'])
|
||||
});
|
||||
|
||||
Zotero.Feed.prototype._initSave = Zotero.Promise.coroutine(function* (env) {
|
||||
|
@ -285,17 +287,78 @@ Zotero.Feed.prototype._saveData = Zotero.Promise.coroutine(function* (env) {
|
|||
});
|
||||
|
||||
Zotero.Feed.prototype._finalizeSave = Zotero.Promise.coroutine(function* (env) {
|
||||
let changedURL = this._changed._feedUrl;
|
||||
|
||||
yield Zotero.Feed._super.prototype._finalizeSave.apply(this, arguments);
|
||||
|
||||
if (env.isNew) {
|
||||
Zotero.Feeds.register(this);
|
||||
} else if (changedURL) {
|
||||
if (!env.isNew && this._previousURL) {
|
||||
// Re-register library if URL changed
|
||||
Zotero.Feeds.unregister(this.libraryID);
|
||||
Zotero.Feeds.register(this);
|
||||
|
||||
let syncedFeeds = Zotero.SyncedSettings.get(Zotero.Libraries.userLibraryID, 'feeds') || {};
|
||||
delete syncedFeeds[this._previousURL];
|
||||
yield Zotero.SyncedSettings.set(Zotero.Libraries.userLibraryID, 'feeds', syncedFeeds);
|
||||
}
|
||||
if (env.isNew || this._previousURL) {
|
||||
Zotero.Feeds.register(this);
|
||||
yield this.storeSyncedSettings();
|
||||
}
|
||||
this._previousURL = null;
|
||||
|
||||
});
|
||||
|
||||
Zotero.Feed.prototype._finalizeErase = Zotero.Promise.coroutine(function* (){
|
||||
let notifierData = {};
|
||||
notifierData[this.libraryID] = {
|
||||
libraryID: this.libraryID
|
||||
};
|
||||
Zotero.Notifier.trigger('delete', 'feed', this.id, notifierData);
|
||||
Zotero.Feeds.unregister(this.libraryID);
|
||||
|
||||
let syncedFeeds = Zotero.SyncedSettings.get(Zotero.Libraries.userLibraryID, 'feeds') || {};
|
||||
delete syncedFeeds[this.url];
|
||||
if (Object.keys(syncedFeeds).length == 0) {
|
||||
yield Zotero.SyncedSettings.clear(Zotero.Libraries.userLibraryID, 'feeds');
|
||||
} else {
|
||||
yield Zotero.SyncedSettings.set(Zotero.Libraries.userLibraryID, 'feeds', syncedFeeds);
|
||||
}
|
||||
|
||||
return Zotero.Feed._super.prototype._finalizeErase.apply(this, arguments);
|
||||
});
|
||||
|
||||
Zotero.Feed.prototype.erase = Zotero.Promise.coroutine(function* (options = {}) {
|
||||
let childItemIDs = yield Zotero.FeedItems.getAll(this.id, false, false, true);
|
||||
yield Zotero.FeedItems.erase(childItemIDs);
|
||||
|
||||
yield Zotero.Feed._super.prototype.erase.call(this, options);
|
||||
});
|
||||
|
||||
Zotero.Feed.prototype.getSyncedSettings = function () {
|
||||
if (!this._syncedSettings) {
|
||||
let syncedFeeds = Zotero.SyncedSettings.get(Zotero.Libraries.userLibraryID, 'feeds') || {};
|
||||
this._syncedSettings = syncedFeeds[this.url];
|
||||
}
|
||||
if (!this._syncedSettings) {
|
||||
this._syncedSettings = {
|
||||
url: this.url,
|
||||
name: this.name,
|
||||
cleanupAfter: this.cleanupAfter,
|
||||
refreshInterval: this.refreshInterval,
|
||||
markedAsRead: {}
|
||||
};
|
||||
}
|
||||
return this._syncedSettings;
|
||||
};
|
||||
|
||||
Zotero.Feed.prototype.setSyncedSettings = Zotero.Promise.coroutine(function* (syncedSettings, store=false) {
|
||||
this._syncedSettings = syncedSettings;
|
||||
if (store) {
|
||||
return this.storeSyncedSettings();
|
||||
}
|
||||
});
|
||||
|
||||
Zotero.Feed.prototype.storeSyncedSettings = Zotero.Promise.coroutine(function* () {
|
||||
let syncedFeeds = Zotero.SyncedSettings.get(Zotero.Libraries.userLibraryID, 'feeds') || {};
|
||||
syncedFeeds[this.url] = this.getSyncedSettings();
|
||||
return Zotero.SyncedSettings.set(Zotero.Libraries.userLibraryID, 'feeds', syncedFeeds);
|
||||
});
|
||||
|
||||
Zotero.Feed.prototype.getExpiredFeedItemIDs = Zotero.Promise.coroutine(function* () {
|
||||
|
@ -304,32 +367,49 @@ Zotero.Feed.prototype.getExpiredFeedItemIDs = Zotero.Promise.coroutine(function*
|
|||
+ "WHERE I.libraryID=? "
|
||||
+ "AND readTime IS NOT NULL "
|
||||
+ "AND julianday('now', 'utc') - (julianday(readTime, 'utc') + ?) > 0";
|
||||
let expiredIDs = yield Zotero.DB.queryAsync(sql, [this.id, {int: this.cleanupAfter}]);
|
||||
return expiredIDs.map(row => row.id);
|
||||
return Zotero.DB.columnQueryAsync(sql, [this.id, {int: this.cleanupAfter}]);
|
||||
});
|
||||
|
||||
Zotero.Feed.prototype.clearExpiredItems = Zotero.Promise.coroutine(function* () {
|
||||
/**
|
||||
* Clearing conditions for an item:
|
||||
* - Has been read at least feed.cleanupAfter earlier AND
|
||||
* - Does not exist in the RSS feed anymore
|
||||
*
|
||||
* If we clear items once they've been read, we may potentially end up
|
||||
* with empty feeds for those that do not update very frequently.
|
||||
*/
|
||||
Zotero.Feed.prototype.clearExpiredItems = Zotero.Promise.coroutine(function* (itemsInFeedIDs) {
|
||||
itemsInFeedIDs = itemsInFeedIDs || new Set();
|
||||
try {
|
||||
// Clear expired items
|
||||
if (this.cleanupAfter) {
|
||||
let expiredItems = yield this.getExpiredFeedItemIDs();
|
||||
Zotero.debug("Cleaning up read feed items...");
|
||||
if (expiredItems.length) {
|
||||
Zotero.debug(expiredItems.join(', '));
|
||||
yield Zotero.FeedItems.forceErase(expiredItems);
|
||||
let toClear = expiredItems;
|
||||
if (itemsInFeedIDs.size) {
|
||||
toClear = [];
|
||||
for (let id of expiredItems) {
|
||||
if (!itemsInFeedIDs.has(id)) {
|
||||
toClear.push(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
Zotero.debug("Clearing up read feed items...");
|
||||
if (toClear.length) {
|
||||
Zotero.debug(toClear.join(', '));
|
||||
yield Zotero.FeedItems.erase(toClear);
|
||||
} else {
|
||||
Zotero.debug("No expired feed items");
|
||||
}
|
||||
}
|
||||
} catch(e) {
|
||||
Zotero.debug("Error clearing expired feed items.");
|
||||
Zotero.debug("Error clearing expired feed items");
|
||||
Zotero.debug(e);
|
||||
}
|
||||
return this.storeSyncedSettings();
|
||||
});
|
||||
|
||||
Zotero.Feed.prototype._updateFeed = Zotero.Promise.coroutine(function* () {
|
||||
var toAdd = [];
|
||||
var createNew = true;
|
||||
var toSave = [], attachmentsToAdd = [], feedItemIDs = new Set();
|
||||
if (this._updating) {
|
||||
return this._updating;
|
||||
}
|
||||
|
@ -338,49 +418,59 @@ Zotero.Feed.prototype._updateFeed = Zotero.Promise.coroutine(function* () {
|
|||
Zotero.Notifier.trigger('statusChanged', 'feed', this.id);
|
||||
this._set('_feedLastCheckError', null);
|
||||
|
||||
yield this.clearExpiredItems();
|
||||
try {
|
||||
let fr = new Zotero.FeedReader(this.url);
|
||||
yield fr.process();
|
||||
let itemIterator = new fr.ItemIterator();
|
||||
let item, processedGUIDs = [];
|
||||
let item, processedGUIDs = new Set();
|
||||
while (item = yield itemIterator.next().value) {
|
||||
// Append id at the end to prevent same item collisions from different feeds
|
||||
// when to terminate item retrieval.
|
||||
item.guid += ":" + this.id;
|
||||
if (item.guid == this.lastGUID) {
|
||||
Zotero.debug("Feed#update: last seen item reached (" + this.lastGUID + ")");
|
||||
Zotero.debug(item);
|
||||
// Don't create new items (expired and deleted), but update existing ones
|
||||
createNew = false;
|
||||
}
|
||||
|
||||
if (processedGUIDs.indexOf(item.guid) != -1) {
|
||||
Zotero.debug("Feed item " + item.guid + " already processed from feed.");
|
||||
if (processedGUIDs.has(item.guid)) {
|
||||
Zotero.debug("Feed item " + item.guid + " already processed from feed");
|
||||
continue;
|
||||
}
|
||||
processedGUIDs.push(item.guid);
|
||||
processedGUIDs.add(item.guid);
|
||||
|
||||
Zotero.debug("New feed item retrieved:", 5);
|
||||
Zotero.debug("Feed item retrieved:", 5);
|
||||
Zotero.debug(item, 5);
|
||||
|
||||
let feedItem = yield Zotero.FeedItems.getAsyncByGUID(item.guid);
|
||||
if (!feedItem && createNew) {
|
||||
if (feedItem) {
|
||||
feedItemIDs.add(feedItem.id);
|
||||
}
|
||||
if (!feedItem) {
|
||||
Zotero.debug("Creating new feed item " + item.guid);
|
||||
feedItem = new Zotero.FeedItem();
|
||||
feedItem.guid = item.guid;
|
||||
feedItem.libraryID = this.id;
|
||||
} else if(! feedItem.isTranslated) {
|
||||
Zotero.debug("Feed item " + item.guid + " already in library.");
|
||||
} else if (!feedItem.isTranslated) {
|
||||
// TODO: maybe handle enclosed items on update better
|
||||
item.enclosedItems = [];
|
||||
|
||||
// TODO figure out a better GUID collision resolution system
|
||||
// that works with sync.
|
||||
if (feedItem.libraryID != this.libraryID) {
|
||||
let otherFeed = Zotero.Feeds.get(feedItem.libraryID);
|
||||
Zotero.debug("Feed item " + feedItem.url + " from " + this.url +
|
||||
" exists in a different feed " + otherFeed.url + ". Skipping");
|
||||
continue;
|
||||
}
|
||||
|
||||
Zotero.debug("Feed item " + item.guid + " already in library");
|
||||
Zotero.debug("Updating metadata");
|
||||
yield feedItem.loadItemData();
|
||||
yield feedItem.loadCreators();
|
||||
} else {
|
||||
// Either has been translated or beyond lastGUID
|
||||
// Not new and has been translated
|
||||
Zotero.debug("Feed item " + item.guid + " is not new and has already been translated. Skipping");
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let enclosedItem of item.enclosedItems) {
|
||||
enclosedItem.parentItem = feedItem;
|
||||
attachmentsToAdd.push(enclosedItem);
|
||||
}
|
||||
|
||||
// Delete invalid data
|
||||
delete item.guid;
|
||||
delete item.enclosedItems;
|
||||
feedItem.fromJSON(item);
|
||||
|
||||
if (!feedItem.hasChanged()) {
|
||||
|
@ -388,7 +478,7 @@ Zotero.Feed.prototype._updateFeed = Zotero.Promise.coroutine(function* () {
|
|||
continue
|
||||
}
|
||||
feedItem.isRead = false;
|
||||
toAdd.push(feedItem);
|
||||
toSave.push(feedItem);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
|
@ -398,18 +488,24 @@ Zotero.Feed.prototype._updateFeed = Zotero.Promise.coroutine(function* () {
|
|||
}
|
||||
this._set('_feedLastCheckError', e.message || 'Error processing feed');
|
||||
}
|
||||
if (toAdd.length) {
|
||||
if (toSave.length) {
|
||||
yield Zotero.DB.executeTransaction(function* () {
|
||||
// Save in reverse order
|
||||
for (let i=toAdd.length-1; i>=0; i--) {
|
||||
// Saving currently has to happen sequentially so as not to violate the
|
||||
// unique constraints in itemDataValues (FIXME)
|
||||
yield toAdd[i].save({skipEditCheck: true});
|
||||
for (let i=toSave.length-1; i>=0; i--) {
|
||||
yield toSave[i].save();
|
||||
}
|
||||
|
||||
});
|
||||
this._set('_feedLastUpdate', Zotero.Date.dateToSQL(new Date(), true));
|
||||
this._set('_feedLastGUID', toAdd[0].guid);
|
||||
}
|
||||
for (let attachment of attachmentsToAdd) {
|
||||
if (attachment.url.indexOf('pdf') != -1 || attachment.contentType.indexOf('pdf') != -1) {
|
||||
attachment.parentItemID = attachment.parentItem.id;
|
||||
attachment.title = Zotero.getString('fileTypes.pdf');
|
||||
yield Zotero.Attachments.linkFromURL(attachment);
|
||||
}
|
||||
}
|
||||
yield this.clearExpiredItems(feedItemIDs);
|
||||
this._set('_feedLastCheck', Zotero.Date.dateToSQL(new Date(), true));
|
||||
yield this.saveTx();
|
||||
yield this.updateUnreadCount();
|
||||
|
@ -427,23 +523,6 @@ Zotero.Feed.prototype.updateFeed = Zotero.Promise.coroutine(function* () {
|
|||
}
|
||||
});
|
||||
|
||||
Zotero.Feed.prototype._finalizeErase = Zotero.Promise.coroutine(function* (){
|
||||
let notifierData = {};
|
||||
notifierData[this.libraryID] = {
|
||||
libraryID: this.libraryID
|
||||
};
|
||||
Zotero.Notifier.trigger('delete', 'feed', this.id, notifierData);
|
||||
Zotero.Feeds.unregister(this.libraryID);
|
||||
return Zotero.Feed._super.prototype._finalizeErase.apply(this, arguments);
|
||||
});
|
||||
|
||||
Zotero.Feed.prototype.erase = Zotero.Promise.coroutine(function* (options = {}) {
|
||||
let childItemIDs = yield Zotero.FeedItems.getAll(this.id, false, false, true);
|
||||
yield Zotero.FeedItems.forceErase(childItemIDs);
|
||||
|
||||
yield Zotero.Feed._super.prototype.erase.call(this, options);
|
||||
});
|
||||
|
||||
Zotero.Feed.prototype.updateUnreadCount = Zotero.Promise.coroutine(function* () {
|
||||
let sql = "SELECT " + Zotero.Feed._unreadCountSQL
|
||||
+ " FROM feeds F JOIN libraries L USING (libraryID)"
|
||||
|
@ -455,3 +534,9 @@ Zotero.Feed.prototype.updateUnreadCount = Zotero.Promise.coroutine(function* ()
|
|||
Zotero.Notifier.trigger('unreadCountUpdated', 'feed', this.id);
|
||||
}
|
||||
});
|
||||
|
||||
Zotero.Feed.prototype.updateFromJSON = Zotero.Promise.coroutine(function* (json) {
|
||||
yield this.updateFeed();
|
||||
yield Zotero.FeedItems.markAsReadByGUID(Object.keys(json.markedAsRead));
|
||||
yield this.updateUnreadCount();
|
||||
});
|
||||
|
|
|
@ -121,8 +121,8 @@ Zotero.FeedItem.prototype.fromJSON = function(json) {
|
|||
d = new Date(d.year, d.month, d.day);
|
||||
Zotero.debug(dateField + " " + JSON.stringify(d), 1);
|
||||
}
|
||||
if (!d) {
|
||||
Zotero.logError("Discarding invalid " + field + " '" + val
|
||||
if (isNaN(d.getTime())) {
|
||||
Zotero.logError("Discarding invalid " + dateField + " '" + json[dateField]
|
||||
+ "' for item " + this.libraryKey);
|
||||
delete json[dateField];
|
||||
continue;
|
||||
|
@ -160,13 +160,6 @@ Zotero.FeedItem.prototype._initSave = Zotero.Promise.coroutine(function* (env) {
|
|||
return proceed;
|
||||
});
|
||||
|
||||
Zotero.FeedItem.prototype.forceSaveTx = function(options) {
|
||||
let newOptions = {};
|
||||
Object.assign(newOptions, options || {});
|
||||
newOptions.skipEditCheck = true;
|
||||
return this.saveTx(newOptions);
|
||||
}
|
||||
|
||||
Zotero.FeedItem.prototype._saveData = Zotero.Promise.coroutine(function* (env) {
|
||||
yield Zotero.FeedItem._super.prototype._saveData.apply(this, arguments);
|
||||
|
||||
|
@ -178,25 +171,38 @@ Zotero.FeedItem.prototype._saveData = Zotero.Promise.coroutine(function* (env) {
|
|||
}
|
||||
});
|
||||
|
||||
Zotero.FeedItem.prototype._finalizeErase = Zotero.Promise.coroutine(function* () {
|
||||
// Set for syncing
|
||||
let feed = Zotero.Feeds.get(this.libraryID);
|
||||
let syncedSettings = feed.getSyncedSettings();
|
||||
delete syncedSettings.markedAsRead[this.guid];
|
||||
yield feed.setSyncedSettings(syncedSettings);
|
||||
|
||||
return Zotero.FeedItem._super.prototype._finalizeErase.apply(this, arguments);
|
||||
});
|
||||
|
||||
Zotero.FeedItem.prototype.toggleRead = Zotero.Promise.coroutine(function* (state) {
|
||||
state = state !== undefined ? !!state : !this.isRead;
|
||||
let changed = this.isRead != state;
|
||||
if (changed) {
|
||||
this.isRead = state;
|
||||
yield this.saveTx({skipEditCheck: true, skipDateModifiedUpdate: true});
|
||||
|
||||
// Set for syncing
|
||||
let feed = Zotero.Feeds.get(this.libraryID);
|
||||
let syncedSettings = feed.getSyncedSettings();
|
||||
if (state) {
|
||||
syncedSettings.markedAsRead[this.guid] = true;
|
||||
} else {
|
||||
delete syncedSettings.markedAsRead[this.guid];
|
||||
}
|
||||
yield feed.setSyncedSettings(syncedSettings, true);
|
||||
|
||||
yield this.saveTx();
|
||||
|
||||
yield feed.updateUnreadCount();
|
||||
}
|
||||
});
|
||||
|
||||
Zotero.FeedItem.prototype.forceEraseTx = function(options) {
|
||||
let newOptions = {};
|
||||
Object.assign(newOptions, options || {});
|
||||
newOptions.skipEditCheck = true;
|
||||
return this.eraseTx(newOptions);
|
||||
};
|
||||
|
||||
/**
|
||||
* Uses the item url to translate an existing feed item.
|
||||
* If libraryID empty, overwrites feed item, otherwise saves
|
||||
|
@ -233,7 +239,7 @@ Zotero.FeedItem.prototype.translate = Zotero.Promise.coroutine(function* (librar
|
|||
// Load document
|
||||
let hiddenBrowser = Zotero.HTTP.processDocuments(
|
||||
this.getField('url'),
|
||||
(item) => deferred.resolve(item),
|
||||
item => deferred.resolve(item),
|
||||
()=>{}, error, true
|
||||
);
|
||||
let doc = yield deferred.promise;
|
||||
|
@ -246,9 +252,9 @@ Zotero.FeedItem.prototype.translate = Zotero.Promise.coroutine(function* (librar
|
|||
translate.setHandler('translators', (me, translators) => deferred.resolve(translators));
|
||||
translate.getTranslators();
|
||||
let translators = yield deferred.promise;
|
||||
if (! translators || !translators.length) {
|
||||
Zotero.debug("No translators detected for FeedItem " + this.id + " with url " + this.getField('url'), 2);
|
||||
throw new Zotero.Error("No translators detected for FeedItem " + this.id + " with url " + this.getField('url'))
|
||||
if (!translators || !translators.length) {
|
||||
Zotero.debug("No translators detected for feed item " + this.id + " with URL " + this.getField('url'), 2);
|
||||
throw new Zotero.Error("No translators detected for feed item " + this.id + " with URL " + this.getField('url'))
|
||||
}
|
||||
translate.setTranslator(translators[0]);
|
||||
|
||||
|
@ -280,7 +286,7 @@ Zotero.FeedItem.prototype.translate = Zotero.Promise.coroutine(function* (librar
|
|||
|
||||
this.fromJSON(itemData);
|
||||
this.isTranslated = true;
|
||||
this.forceSaveTx();
|
||||
yield this.saveTx();
|
||||
|
||||
return this;
|
||||
});
|
||||
|
|
|
@ -38,13 +38,13 @@ Zotero.FeedItems = new Proxy(function() {
|
|||
Zotero.defineProperty(Zotero.Items, '_primaryDataSQLParts', {
|
||||
get: function() {
|
||||
let obj = zi_primaryDataSQLParts.call(this);
|
||||
obj.feedItemGUID = "FeI.guid AS feedItemGUID";
|
||||
obj.feedItemReadTime = "FeI.readTime AS feedItemReadTime";
|
||||
obj.feedItemTranslatedTime = "FeI.translatedTime AS feedItemTranslatedTime";
|
||||
obj.feedItemGUID = "FI.guid AS feedItemGUID";
|
||||
obj.feedItemReadTime = "FI.readTime AS feedItemReadTime";
|
||||
obj.feedItemTranslatedTime = "FI.translatedTime AS feedItemTranslatedTime";
|
||||
return obj;
|
||||
}
|
||||
}, {lazy: true});
|
||||
Zotero.Items._primaryDataSQLFrom += " LEFT JOIN feedItems FeI ON (FeI.itemID=O.itemID)";
|
||||
Zotero.Items._primaryDataSQLFrom += " LEFT JOIN feedItems FI ON (FI.itemID=O.itemID)";
|
||||
|
||||
let zi_getObjectForRow = Zotero.Items._getObjectForRow;
|
||||
Zotero.Items._getObjectForRow = function(row) {
|
||||
|
@ -58,7 +58,7 @@ Zotero.FeedItems = new Proxy(function() {
|
|||
this.getIDFromGUID = Zotero.Promise.coroutine(function* (guid) {
|
||||
if (_idCache[guid] !== undefined) return _idCache[guid];
|
||||
|
||||
id = yield Zotero.DB.valueQueryAsync('SELECT itemID FROM feedItems WHERE guid=?', [guid]);
|
||||
let id = yield Zotero.DB.valueQueryAsync('SELECT itemID FROM feedItems WHERE guid=?', [guid]);
|
||||
if (!id) return false;
|
||||
|
||||
this._setGUIDMapping(guid, id);
|
||||
|
@ -95,6 +95,38 @@ Zotero.FeedItems = new Proxy(function() {
|
|||
return this.getAsync(id);
|
||||
});
|
||||
|
||||
this.getMarkedAsRead = Zotero.Promise.coroutine(function* (libraryID, onlyGUIDs=false) {
|
||||
let sql = "SELECT " + (onlyGUIDs ? "guid " : "itemID ") +
|
||||
"FROM feedItems FI " +
|
||||
"JOIN items I USING (itemID) " +
|
||||
"WHERE libraryID=? AND readTime IS NOT NULL";
|
||||
let ids = yield Zotero.DB.columnQueryAsync(sql, [libraryID]);
|
||||
if (onlyGUIDs) {
|
||||
return ids;
|
||||
}
|
||||
return Zotero.FeedItems.getAsync(ids);
|
||||
|
||||
});
|
||||
|
||||
/**
|
||||
* Used on restore from sync
|
||||
*/
|
||||
this.markAsReadByGUID = Zotero.Promise.coroutine(function* (guids) {
|
||||
if (! Array.isArray(guids)) {
|
||||
throw new Error('guids must be an array in Zotero.FeedItems.toggleReadByID');
|
||||
}
|
||||
let ids = [];
|
||||
Zotero.debug("Marking items as read");
|
||||
Zotero.debug(guids);
|
||||
for (let guid of guids) {
|
||||
let id = yield this.getIDFromGUID(guid);
|
||||
if (id) {
|
||||
ids.push(id);
|
||||
}
|
||||
}
|
||||
return this.toggleReadByID(ids, true);
|
||||
});
|
||||
|
||||
this.toggleReadByID = Zotero.Promise.coroutine(function* (ids, state) {
|
||||
var feedsToUpdate = new Set();
|
||||
if (!Array.isArray(ids)) {
|
||||
|
@ -115,24 +147,31 @@ Zotero.FeedItems = new Proxy(function() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
yield Zotero.DB.executeTransaction(function() {
|
||||
for (let i=0; i<items.length; i++) {
|
||||
items[i].isRead = state;
|
||||
|
||||
// Set for syncing
|
||||
let feed = Zotero.Feeds.get(items[i].libraryID);
|
||||
let syncedSettings = feed.getSyncedSettings();
|
||||
if (state) {
|
||||
syncedSettings.markedAsRead[items[i].guid] = true;
|
||||
} else {
|
||||
delete syncedSettings.markedAsRead[items[i].guid];
|
||||
}
|
||||
yield feed.setSyncedSettings(syncedSettings);
|
||||
|
||||
yield items[i].save({skipEditCheck: true});
|
||||
feedsToUpdate.add(items[i].libraryID);
|
||||
feedsToUpdate.add(feed);
|
||||
}
|
||||
});
|
||||
for (let feedID of feedsToUpdate) {
|
||||
let feed = Zotero.Feeds.get(feedID);
|
||||
yield feed.updateUnreadCount();
|
||||
|
||||
for (let feed of feedsToUpdate) {
|
||||
yield Zotero.Promise.all([feed.updateUnreadCount(), feed.storeSyncedSettings()]);
|
||||
}
|
||||
});
|
||||
|
||||
this.forceErase = function(ids, options = {}) {
|
||||
options.skipEditCheck = true;
|
||||
return this.erase(ids, options);
|
||||
};
|
||||
|
||||
return this;
|
||||
}.call({}),
|
||||
|
||||
|
|
|
@ -68,6 +68,51 @@ Zotero.Feeds = new function() {
|
|||
delete this._cache.urlByLibraryID[libraryID];
|
||||
delete this._cache.libraryIDByURL[url];
|
||||
}
|
||||
|
||||
this.init = function () {
|
||||
return this.scheduleNextFeedCheck();
|
||||
}
|
||||
|
||||
this.restoreFromJSON = Zotero.Promise.coroutine(function* (json, merge=false) {
|
||||
Zotero.debug("Restoring feeds from remote JSON");
|
||||
Zotero.debug(json);
|
||||
if (merge) {
|
||||
let syncedFeeds = Zotero.SyncedSettings.get(Zotero.Libraries.userLibraryID, 'feeds');
|
||||
for (let url in json) {
|
||||
if (syncedFeeds[url]) {
|
||||
syncedFeeds[url].name = json[url].name;
|
||||
syncedFeeds[url].cleanupAfter = json[url].cleanupAfter;
|
||||
syncedFeeds[url].refreshInterval = json[url].refreshInterval;
|
||||
for (let guid in json[url].markedAsRead) {
|
||||
syncedFeeds[url].markedAsRead[guid] = true;
|
||||
}
|
||||
} else {
|
||||
syncedFeeds[url] = json[url];
|
||||
}
|
||||
}
|
||||
json = syncedFeeds;
|
||||
}
|
||||
yield Zotero.SyncedSettings.set(Zotero.Libraries.userLibraryID, 'feeds', json);
|
||||
//
|
||||
let feeds = Zotero.Feeds.getAll();
|
||||
for (let feed of feeds) {
|
||||
if (json[feed.url]) {
|
||||
Zotero.debug("Feed " + feed.url + " is being updated from remote JSON");
|
||||
yield feed.updateFromJSON(json[feed.url]);
|
||||
delete json[feed.url];
|
||||
} else {
|
||||
Zotero.debug("Feed " + feed.url + " does not exist in remote JSON. Deleting");
|
||||
yield feed.eraseTx();
|
||||
}
|
||||
}
|
||||
// Because existing json[feed.url] got deleted, `json` now only contains new feeds
|
||||
for (let url in json) {
|
||||
Zotero.debug("Feed " + url + " is being created from remote JSON");
|
||||
let feed = new Zotero.Feed(json[url]);
|
||||
yield feed.saveTx();
|
||||
yield feed.updateFromJSON(json[url]);
|
||||
}
|
||||
});
|
||||
|
||||
this.getByURL = function(urls) {
|
||||
if (!this._cache) throw new Error("Zotero.Feeds cache is not initialized");
|
||||
|
@ -78,17 +123,16 @@ Zotero.Feeds = new function() {
|
|||
asArray = false;
|
||||
}
|
||||
|
||||
let libraryIDs = Array(urls.length);
|
||||
let feeds = new Array(urls.length);
|
||||
for (let i=0; i<urls.length; i++) {
|
||||
let libraryID = this._cache.libraryIDByURL[urls[i]];
|
||||
if (!libraryID) {
|
||||
throw new Error('Feed with url ' + urls[i] + ' not registered in feed cache');
|
||||
return
|
||||
}
|
||||
|
||||
libraryIDs[i] = libraryID;
|
||||
feeds[i] = Zotero.Libraries.get(libraryID);
|
||||
}
|
||||
|
||||
let feeds = Zotero.Libraries.get(libraryIDs);
|
||||
return asArray ? feeds : feeds[0];
|
||||
}
|
||||
|
||||
|
@ -118,7 +162,7 @@ Zotero.Feeds = new function() {
|
|||
|
||||
let globalFeedCheckDelay = Zotero.Promise.resolve();
|
||||
this.scheduleNextFeedCheck = Zotero.Promise.coroutine(function* () {
|
||||
Zotero.debug("Scheduling next feed update.");
|
||||
Zotero.debug("Scheduling next feed update");
|
||||
let sql = "SELECT ( CASE "
|
||||
+ "WHEN lastCheck IS NULL THEN 0 "
|
||||
+ "ELSE strftime('%s', lastCheck) + refreshInterval*3600 - strftime('%s', 'now') "
|
||||
|
@ -149,7 +193,7 @@ Zotero.Feeds = new function() {
|
|||
throw e;
|
||||
});
|
||||
} else {
|
||||
Zotero.debug("No feeds with auto-update.");
|
||||
Zotero.debug("No feeds with auto-update");
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -165,7 +209,7 @@ Zotero.Feeds = new function() {
|
|||
yield feed._updateFeed();
|
||||
}
|
||||
|
||||
Zotero.debug("All feed updates done.");
|
||||
Zotero.debug("All feed updates done");
|
||||
this.scheduleNextFeedCheck();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -133,10 +133,10 @@ Zotero.Items = function() {
|
|||
* @param {Integer} libraryID
|
||||
* @param {Boolean} [onlyTopLevel=false] If true, don't include child items
|
||||
* @param {Boolean} [includeDeleted=false] If true, include deleted items
|
||||
* @param {Boolean} [onlyIDs=false] If true, resolves only with IDs
|
||||
* @param {Boolean} [asIDs=false] If true, resolves only with IDs
|
||||
* @return {Promise<Array<Zotero.Item|Integer>>}
|
||||
*/
|
||||
this.getAll = Zotero.Promise.coroutine(function* (libraryID, onlyTopLevel, includeDeleted, onlyIDs=false) {
|
||||
this.getAll = Zotero.Promise.coroutine(function* (libraryID, onlyTopLevel, includeDeleted, asIDs=false) {
|
||||
var sql = 'SELECT A.itemID FROM items A';
|
||||
if (onlyTopLevel) {
|
||||
sql += ' LEFT JOIN itemNotes B USING (itemID) '
|
||||
|
@ -151,7 +151,7 @@ Zotero.Items = function() {
|
|||
}
|
||||
sql += " AND libraryID=?";
|
||||
var ids = yield Zotero.DB.columnQueryAsync(sql, libraryID);
|
||||
if (onlyIDs) {
|
||||
if (asIDs) {
|
||||
return ids;
|
||||
}
|
||||
return this.getAsync(ids);
|
||||
|
|
|
@ -462,6 +462,8 @@ Zotero.FeedReader._getFeedItem = function(feedEntry, feedInfo) {
|
|||
|
||||
Zotero.FeedReader._guessItemType(item);
|
||||
|
||||
item.enclosedItems = Zotero.FeedReader._getEnclosedItems(feedEntry);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
|
@ -501,3 +503,19 @@ Zotero.FeedReader._getFeedField = function(feedEntry, field, namespace) {
|
|||
|
||||
return;
|
||||
}
|
||||
|
||||
Zotero.FeedReader._getEnclosedItems = function(feedEntry) {
|
||||
var enclosedItems = [];
|
||||
|
||||
if (feedEntry.enclosures) {
|
||||
for (let i = 0; i < feedEntry.enclosures.length; i++) {
|
||||
let elem = feedEntry.enclosures.queryElementAt(0, Components.interfaces.nsIPropertyBag2);
|
||||
if (elem.get('url')) {
|
||||
let enclosedItem = {url: elem.get('url'), contentType: elem.get('type') || ''};
|
||||
enclosedItems.push(enclosedItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return enclosedItems;
|
||||
}
|
||||
|
|
|
@ -98,6 +98,7 @@ Zotero.ItemTreeView.prototype.setTree = Zotero.Promise.coroutine(function* (tree
|
|||
}
|
||||
|
||||
this._treebox = treebox;
|
||||
this.setSortColumn();
|
||||
|
||||
if (this._ownerDocument.defaultView.ZoteroPane_Local) {
|
||||
this._ownerDocument.defaultView.ZoteroPane_Local.setItemsPaneMessage(Zotero.getString('pane.items.loading'));
|
||||
|
@ -259,6 +260,43 @@ Zotero.ItemTreeView.prototype.setTree = Zotero.Promise.coroutine(function* (tree
|
|||
});
|
||||
|
||||
|
||||
Zotero.ItemTreeView.prototype.setSortColumn = function() {
|
||||
var dir, col, currentCol, currentDir;
|
||||
|
||||
for (let i=0, len=this._treebox.columns.count; i<len; i++) {
|
||||
let column = this._treebox.columns.getColumnAt(i);
|
||||
if (column.element.getAttribute('sortActive')) {
|
||||
currentCol = column;
|
||||
currentDir = column.element.getAttribute('sortDirection');
|
||||
column.element.removeAttribute('sortActive');
|
||||
column.element.removeAttribute('sortDirection');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let colID = Zotero.Prefs.get('itemTree.sortColumnID');
|
||||
// Restore previous sort setting (feed -> non-feed)
|
||||
if (! this.collectionTreeRow.isFeed() && colID) {
|
||||
col = this._treebox.columns.getNamedColumn(colID);
|
||||
dir = Zotero.Prefs.get('itemTree.sortDirection');
|
||||
Zotero.Prefs.clear('itemTree.sortColumnID');
|
||||
Zotero.Prefs.clear('itemTree.sortDirection');
|
||||
// No previous sort setting stored, so store it (non-feed -> feed)
|
||||
} else if (this.collectionTreeRow.isFeed() && !colID && currentCol) {
|
||||
Zotero.Prefs.set('itemTree.sortColumnID', currentCol.id);
|
||||
Zotero.Prefs.set('itemTree.sortDirection', currentDir);
|
||||
// Retain current sort setting (non-feed -> non-feed)
|
||||
} else {
|
||||
col = currentCol;
|
||||
dir = currentDir;
|
||||
}
|
||||
if (col) {
|
||||
col.element.setAttribute('sortActive', true);
|
||||
col.element.setAttribute('sortDirection', dir);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reload the rows from the data access methods
|
||||
* (doesn't call the tree.invalidate methods, etc.)
|
||||
|
@ -1228,6 +1266,9 @@ Zotero.ItemTreeView.prototype.isSorted = function()
|
|||
}
|
||||
|
||||
Zotero.ItemTreeView.prototype.cycleHeader = function (column) {
|
||||
if (this.collectionTreeRow.isFeed()) {
|
||||
return;
|
||||
}
|
||||
for(var i=0, len=this._treebox.columns.count; i<len; i++)
|
||||
{
|
||||
col = this._treebox.columns.getColumnAt(i);
|
||||
|
@ -2136,8 +2177,8 @@ Zotero.ItemTreeView.prototype.getSortFields = function () {
|
|||
* Returns 'ascending' or 'descending'
|
||||
*/
|
||||
Zotero.ItemTreeView.prototype.getSortDirection = function() {
|
||||
if (this.collectionTreeRow.isFeed) {
|
||||
return Zotero.Prefs.get('feeds.sortAsc') ? 'ascending' : 'descending';
|
||||
if (this.collectionTreeRow.isFeed()) {
|
||||
return Zotero.Prefs.get('feeds.sortAscending') ? 'ascending' : 'descending';
|
||||
}
|
||||
var column = this._treebox.columns.getSortedColumn();
|
||||
if (!column) {
|
||||
|
|
|
@ -2216,7 +2216,7 @@ Zotero.Schema = new function(){
|
|||
// Feeds
|
||||
yield Zotero.DB.queryAsync("DROP TABLE IF EXISTS feeds");
|
||||
yield Zotero.DB.queryAsync("DROP TABLE IF EXISTS feedItems");
|
||||
yield Zotero.DB.queryAsync("CREATE TABLE feeds (\n libraryID INTEGER PRIMARY KEY,\n name TEXT NOT NULL,\n url TEXT NOT NULL UNIQUE,\n lastUpdate TIMESTAMP,\n lastCheck TIMESTAMP,\n lastCheckError TEXT,\n lastGUID TEXT,\n cleanupAfter INT,\n refreshInterval INT,\n FOREIGN KEY (libraryID) REFERENCES libraries(libraryID) ON DELETE CASCADE\n)");
|
||||
yield Zotero.DB.queryAsync("CREATE TABLE feeds (\n libraryID INTEGER PRIMARY KEY,\n name TEXT NOT NULL,\n url TEXT NOT NULL UNIQUE,\n lastUpdate TIMESTAMP,\n lastCheck TIMESTAMP,\n lastCheckError TEXT,\n cleanupAfter INT,\n refreshInterval INT,\n FOREIGN KEY (libraryID) REFERENCES libraries(libraryID) ON DELETE CASCADE\n)");
|
||||
yield Zotero.DB.queryAsync("CREATE TABLE feedItems (\n itemID INTEGER PRIMARY KEY,\n guid TEXT NOT NULL UNIQUE,\n readTime TIMESTAMP,\n translatedTime TIMESTAMP,\n FOREIGN KEY (itemID) REFERENCES items(itemID) ON DELETE CASCADE\n)");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ Zotero.Sync.Storage.Local = {
|
|||
switch (libraryType) {
|
||||
case 'user':
|
||||
case 'publications':
|
||||
case 'feed':
|
||||
return Zotero.Prefs.get("sync.storage.protocol") == 'webdav' ? 'webdav' : 'zfs';
|
||||
|
||||
case 'group':
|
||||
|
|
|
@ -879,6 +879,7 @@ Zotero.Sync.Data.Local = {
|
|||
Zotero.debug("SAVING " + json.key + " WITH SYNCED");
|
||||
Zotero.debug(obj.version);
|
||||
yield obj.save({
|
||||
skipEditCheck: true,
|
||||
skipDateModifiedUpdate: true,
|
||||
skipSelect: true,
|
||||
errorHandler: function (e) {
|
||||
|
|
|
@ -242,11 +242,12 @@ Zotero.Sync.Runner_Module = function (options = {}) {
|
|||
// Prompt if library empty and there is no userID stored
|
||||
this.checkEmptyLibrary = Zotero.Promise.coroutine(function* (keyInfo) {
|
||||
let library = Zotero.Libraries.userLibrary;
|
||||
let feeds = Zotero.Feeds.getAll();
|
||||
let userID = Zotero.Users.getCurrentUserID();
|
||||
|
||||
if (!userID) {
|
||||
let hasItems = yield library.hasItems();
|
||||
if (!hasItems) {
|
||||
if (!hasItems && feeds.length <= 0) {
|
||||
let ps = Services.prompt;
|
||||
let index = ps.confirmEx(
|
||||
null,
|
||||
|
|
|
@ -626,7 +626,8 @@ Components.utils.import("resource://gre/modules/osfile.jsm");
|
|||
yield Zotero.Searches.init();
|
||||
yield Zotero.Creators.init();
|
||||
yield Zotero.Groups.init();
|
||||
yield Zotero.Relations.init()
|
||||
yield Zotero.Relations.init();
|
||||
yield Zotero.Feeds.init();
|
||||
|
||||
let libraryIDs = Zotero.Libraries.getAll().map(x => x.libraryID);
|
||||
for (let libraryID of libraryIDs) {
|
||||
|
|
|
@ -1381,9 +1381,10 @@ var ZoteroPane = new function()
|
|||
}
|
||||
|
||||
if (item.isFeedItem) {
|
||||
if (! item.isTranslated) {
|
||||
item.translate();
|
||||
}
|
||||
// Too slow for now
|
||||
// if (!item.isTranslated) {
|
||||
// item.translate();
|
||||
// }
|
||||
this.startItemReadTimeout(item.id);
|
||||
}
|
||||
}
|
||||
|
@ -1756,7 +1757,7 @@ var ZoteroPane = new function()
|
|||
return;
|
||||
}
|
||||
|
||||
if (!this.canEdit()) {
|
||||
if (!this.canEdit() && !collectionTreeRow.isFeed()) {
|
||||
this.displayCannotEditLibraryMessage();
|
||||
return;
|
||||
}
|
||||
|
@ -1874,7 +1875,7 @@ var ZoteroPane = new function()
|
|||
}
|
||||
|
||||
if (this.collectionsView.selection.count > 0) {
|
||||
var row = this.collectionsView.getRow(this.collectionsView.selection.currentIndex);
|
||||
var row = this.collectionsView.selectedTreeRow;
|
||||
|
||||
if (row.isCollection()) {
|
||||
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
||||
|
@ -1924,7 +1925,7 @@ var ZoteroPane = new function()
|
|||
this.markFeedRead = Zotero.Promise.coroutine(function* () {
|
||||
if (!this.collectionsView.selection.count) return;
|
||||
|
||||
let feed = this.collectionsView.getRow(this.collectionsView.selection.currentIndex).ref;
|
||||
let feed = this.collectionsView.selectedTreeRow.ref;
|
||||
let feedItemIDs = yield Zotero.FeedItems.getAll(feed.libraryID, true, false, true);
|
||||
yield Zotero.FeedItems.toggleReadByID(feedItemIDs, true);
|
||||
});
|
||||
|
@ -1933,7 +1934,7 @@ var ZoteroPane = new function()
|
|||
this.editSelectedFeed = Zotero.Promise.coroutine(function* () {
|
||||
if (!this.collectionsView.selection.count) return;
|
||||
|
||||
let feed = this.collectionsView.getRow(this.collectionsView.selection.currentIndex).ref;
|
||||
let feed = this.collectionsView.selectedTreeRow.ref;
|
||||
let data = {
|
||||
url: feed.url,
|
||||
title: feed.name,
|
||||
|
@ -1954,7 +1955,7 @@ var ZoteroPane = new function()
|
|||
this.refreshFeed = function() {
|
||||
if (!this.collectionsView.selection.count) return;
|
||||
|
||||
let feed = this.collectionsView.getRow(this.collectionsView.selection.currentIndex).ref;
|
||||
let feed = this.collectionsView.selectedTreeRow.ref;
|
||||
|
||||
return feed.updateFeed();
|
||||
}
|
||||
|
@ -2297,29 +2298,15 @@ var ZoteroPane = new function()
|
|||
m.sep1,
|
||||
m.markReadFeed,
|
||||
m.editSelectedFeed,
|
||||
m.deleteCollectionAndItems,
|
||||
m.sep2,
|
||||
m.exportCollection,
|
||||
m.createBibCollection,
|
||||
m.loadReport
|
||||
m.deleteCollectionAndItems
|
||||
];
|
||||
|
||||
if (!this.itemsView.rowCount) {
|
||||
disable = [m.createBibCollection, m.loadReport]
|
||||
if (this.collectionsView.isContainerEmpty(this.collectionsView.selection.currentIndex)) {
|
||||
disable.push(m.exportCollection);
|
||||
}
|
||||
}
|
||||
|
||||
if (collectionTreeRow.ref.unreadCount == 0) {
|
||||
disable.push(m.markReadFeed);
|
||||
}
|
||||
|
||||
// Adjust labels
|
||||
menu.childNodes[m.deleteCollectionAndItems].setAttribute('label', Zotero.getString('pane.collections.menu.delete.feedAndItems'));
|
||||
menu.childNodes[m.exportCollection].setAttribute('label', Zotero.getString('pane.collections.menu.export.feed'));
|
||||
menu.childNodes[m.createBibCollection].setAttribute('label', Zotero.getString('pane.collections.menu.createBib.feed'));
|
||||
menu.childNodes[m.loadReport].setAttribute('label', Zotero.getString('pane.collections.menu.generateReport.feed'));
|
||||
}
|
||||
else if (collectionTreeRow.isSearch()) {
|
||||
show = [
|
||||
|
@ -2464,11 +2451,13 @@ var ZoteroPane = new function()
|
|||
else if (collectionTreeRow.isPublications()) {
|
||||
show.push(m.deleteFromLibrary);
|
||||
}
|
||||
else if (! collectionTreeRow.isFeed()) {
|
||||
else if (!collectionTreeRow.isFeed()) {
|
||||
show.push(m.moveToTrash);
|
||||
}
|
||||
|
||||
show.push(m.sep3, m.exportItems, m.createBib, m.loadReport);
|
||||
|
||||
if(!collectionTreeRow.isFeed()) {
|
||||
show.push(m.sep3, m.exportItems, m.createBib, m.loadReport);
|
||||
}
|
||||
|
||||
if (this.itemsView.selection.count > 0) {
|
||||
// Multiple items selected
|
||||
|
@ -2523,9 +2512,9 @@ var ZoteroPane = new function()
|
|||
if (canMarkRead) {
|
||||
show.push(m.toggleRead);
|
||||
if (markUnread) {
|
||||
menu.childNodes[m.toggleRead].setAttribute('label', Zotero.getString('pane.collections.menu.toggleRead.markUnread'));
|
||||
menu.childNodes[m.toggleRead].setAttribute('label', Zotero.getString('pane.item.markAsUnread'));
|
||||
} else {
|
||||
menu.childNodes[m.toggleRead].setAttribute('label', Zotero.getString('pane.collections.menu.toggleRead.markRead'));
|
||||
menu.childNodes[m.toggleRead].setAttribute('label', Zotero.getString('pane.item.markAsRead'));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2618,9 +2607,9 @@ var ZoteroPane = new function()
|
|||
else if (item.isFeedItem) {
|
||||
show.push(m.toggleRead);
|
||||
if (item.isRead) {
|
||||
menu.childNodes[m.toggleRead].setAttribute('label', Zotero.getString('pane.collections.menu.toggleRead.markUnread'));
|
||||
menu.childNodes[m.toggleRead].setAttribute('label', Zotero.getString('pane.item.markAsUnread'));
|
||||
} else {
|
||||
menu.childNodes[m.toggleRead].setAttribute('label', Zotero.getString('pane.collections.menu.toggleRead.markRead'));
|
||||
menu.childNodes[m.toggleRead].setAttribute('label', Zotero.getString('pane.item.markAsRead'));
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -2652,13 +2641,14 @@ var ZoteroPane = new function()
|
|||
m.moveToTrash, m.deleteFromLibrary, m.exportItems, m.createBib, m.loadReport);
|
||||
}
|
||||
|
||||
if (!collectionTreeRow.editable || collectionTreeRow.isPublications()) {
|
||||
if ((!collectionTreeRow.editable || collectionTreeRow.isPublications()) && !collectionTreeRow.isFeed()) {
|
||||
for (let i in m) {
|
||||
// Still show export/bib/report for non-editable views
|
||||
// Still allow export/bib/report/read for non-editable views
|
||||
switch (i) {
|
||||
case 'exportItems':
|
||||
case 'createBib':
|
||||
case 'loadReport':
|
||||
case 'toggleRead':
|
||||
continue;
|
||||
}
|
||||
if (isTrash) {
|
||||
|
|
|
@ -40,9 +40,11 @@
|
|||
<!ENTITY zotero.preferences.groups.tags "tags">
|
||||
|
||||
<!ENTITY zotero.preferences.feeds "Feeds">
|
||||
<!ENTITY zotero.preferences.feeds.sorting "Sorting">
|
||||
<!ENTITY zotero.preferences.feeds.sorting.newestFirst "Newest Item First">
|
||||
<!ENTITY zotero.preferences.feeds.sorting.oldestFirst "Oldest Item First">
|
||||
<!ENTITY zotero.preferences.feeds.sorting.label1 "Sorting:">
|
||||
<!ENTITY zotero.preferences.feeds.sorting.label2 "Item First">
|
||||
<!ENTITY zotero.preferences.feeds.sorting.newest "Newest">
|
||||
<!ENTITY zotero.preferences.feeds.sorting.oldest "Oldest">
|
||||
<!ENTITY zotero.preferences.feeds.feedDefaults "Feed Defaults">
|
||||
|
||||
<!ENTITY zotero.preferences.openurl.caption "OpenURL">
|
||||
|
||||
|
|
|
@ -108,7 +108,7 @@
|
|||
<!ENTITY zotero.toolbar.newFeed.label "New Feed…">
|
||||
<!ENTITY zotero.toolbar.refreshFeed.label "Refresh Feed">
|
||||
<!ENTITY zotero.toolbar.editFeed.label "Edit Feed…">
|
||||
<!ENTITY zotero.toolbar.markFeedRead.label "Mark Feed Read">
|
||||
<!ENTITY zotero.toolbar.markFeedRead.label "Mark Feed As Read">
|
||||
<!ENTITY zotero.toolbar.newGroup "New Group…">
|
||||
<!ENTITY zotero.toolbar.newSubcollection.label "New Subcollection…">
|
||||
<!ENTITY zotero.toolbar.newSavedSearch.label "New Saved Search…">
|
||||
|
@ -262,11 +262,10 @@
|
|||
|
||||
<!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.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.cleanupAfter.label1 "Remove read articles after ">
|
||||
<!ENTITY zotero.feedSettings.cleanupAfter.label2 "day(s)">
|
||||
|
||||
|
|
|
@ -161,8 +161,8 @@ pane.collections.delete = Are you sure you want to delete the selected collect
|
|||
pane.collections.delete.keepItems = Items within this collection will not be deleted.
|
||||
pane.collections.deleteWithItems.title = Delete Collection and Items
|
||||
pane.collections.deleteWithItems = Are you sure you want to delete the selected collection and move all items within it to the Trash?
|
||||
pane.feed.deleteWithItems.title = Delete Feed and Items
|
||||
pane.feed.deleteWithItems = Are you sure you want to delete the selected feed and all items within it?
|
||||
pane.feed.deleteWithItems.title = Unsubscribe
|
||||
pane.feed.deleteWithItems = Are you sure you want to unsubscribe from this feed?
|
||||
|
||||
pane.collections.deleteSearch.title = Delete Search
|
||||
pane.collections.deleteSearch = Are you sure you want to delete the selected search?
|
||||
|
@ -188,7 +188,7 @@ pane.collections.menu.edit.feed = Edit Feed…
|
|||
pane.collections.menu.delete.collection = Delete Collection…
|
||||
pane.collections.menu.delete.collectionAndItems = Delete Collection and Items…
|
||||
pane.collections.menu.delete.savedSearch = Delete Saved Search…
|
||||
pane.collections.menu.delete.feedAndItems = Delete Feed and Items…
|
||||
pane.collections.menu.delete.feedAndItems = Unsubscribe From Feed…
|
||||
pane.collections.menu.export.collection = Export Collection…
|
||||
pane.collections.menu.export.savedSearch = Export Saved Search…
|
||||
pane.collections.menu.export.feed = Export Feed…
|
||||
|
@ -201,8 +201,6 @@ pane.collections.menu.generateReport.savedSearch = Generate Report from Saved Se
|
|||
pane.collections.menu.generateReport.feed = Generate Report from Feed…
|
||||
|
||||
pane.collections.menu.refresh.feed = Refresh Feed
|
||||
pane.collections.menu.toggleRead.markRead = Mark Read
|
||||
pane.collections.menu.toggleRead.markUnread = Mark Unread
|
||||
|
||||
pane.tagSelector.rename.title = Rename Tag
|
||||
pane.tagSelector.rename.message = Please enter a new name for this tag.\n\nThe tag will be changed in all associated items.
|
||||
|
@ -273,6 +271,8 @@ pane.item.duplicates.writeAccessRequired = Library write access is required to m
|
|||
pane.item.duplicates.onlyTopLevel = Only top-level full items can be merged.
|
||||
pane.item.duplicates.onlySameItemType = Merged items must all be of the same item type.
|
||||
|
||||
pane.item.markAsRead = Mark As Read
|
||||
pane.item.markAsUnread = Mark As Unread
|
||||
pane.item.changeType.title = Change Item Type
|
||||
pane.item.changeType.text = Are you sure you want to change the item type?\n\nThe following fields will be lost:
|
||||
pane.item.defaultFirstName = first
|
||||
|
@ -506,7 +506,7 @@ save.link.error = An error occurred while saving this link.
|
|||
save.error.cannotMakeChangesToCollection = You cannot make changes to the currently selected collection.
|
||||
save.error.cannotAddFilesToCollection = You cannot add files to the currently selected collection.
|
||||
save.error.cannotAddToMyPublications = You cannot save items directly to My Publications. To add items to My Publications, drag them from another library.
|
||||
save.error.cannotAddToFeed = You cannot save items directly to feeds.
|
||||
save.error.cannotAddToFeed = You cannot save items to feeds.
|
||||
|
||||
ingester.saveToZotero = Save to Zotero
|
||||
ingester.saveToZoteroUsing = Save to Zotero using "%S"
|
||||
|
|
|
@ -25,6 +25,11 @@
|
|||
padding: 1.5em .25em .25em;
|
||||
}
|
||||
|
||||
#zotero-view-item.no-tabs
|
||||
{
|
||||
padding: .25em .25em .25em;
|
||||
}
|
||||
|
||||
#zotero-view-item > tabpanel > *
|
||||
{
|
||||
overflow: auto;
|
||||
|
|
|
@ -450,6 +450,10 @@ label.zotero-text-link {
|
|||
transform: none;
|
||||
}
|
||||
|
||||
#zotero-feed-settings grid hbox:first-child {
|
||||
-moz-box-pack: end;
|
||||
}
|
||||
|
||||
/* BEGIN 2X BLOCK -- DO NOT EDIT MANUALLY -- USE 2XIZE */
|
||||
@media (min-resolution: 1.5dppx) {
|
||||
#zotero-toolbar-save-button,#zotero-toolbar-save-button-single { list-style-image: url("chrome://zotero/skin/treeitem-webpage@2x.png"); }
|
||||
|
|
|
@ -52,7 +52,9 @@ pref("extensions.zotero.groups.copyChildFileAttachments", true);
|
|||
pref("extensions.zotero.groups.copyChildNotes", true);
|
||||
pref("extensions.zotero.groups.copyTags", true);
|
||||
|
||||
pref("extensions.zotero.feeds.sortAsc", false);
|
||||
pref("extensions.zotero.feeds.sortAscending", false);
|
||||
pref("extensions.zotero.feeds.defaultTTL", 1);
|
||||
pref("extensions.zotero.feeds.defaultCleanupAfter", 2);
|
||||
|
||||
pref("extensions.zotero.backup.numBackups", 2);
|
||||
pref("extensions.zotero.backup.interval", 1440);
|
||||
|
|
|
@ -203,7 +203,6 @@ CREATE TABLE feeds (
|
|||
lastUpdate TIMESTAMP,
|
||||
lastCheck TIMESTAMP,
|
||||
lastCheckError TEXT,
|
||||
lastGUID TEXT,
|
||||
cleanupAfter INT,
|
||||
refreshInterval INT,
|
||||
FOREIGN KEY (libraryID) REFERENCES libraries(libraryID) ON DELETE CASCADE
|
||||
|
|
|
@ -385,11 +385,7 @@ function createUnsavedDataObject(objectType, params = {}) {
|
|||
|
||||
var createDataObject = Zotero.Promise.coroutine(function* (objectType, params = {}, saveOptions) {
|
||||
var obj = createUnsavedDataObject(objectType, params);
|
||||
if (objectType == 'feedItem') {
|
||||
yield obj.forceSaveTx(saveOptions);
|
||||
} else {
|
||||
yield obj.saveTx(saveOptions);
|
||||
}
|
||||
yield obj.saveTx(saveOptions);
|
||||
return obj;
|
||||
});
|
||||
|
||||
|
@ -453,8 +449,12 @@ function getTestDataDirectory() {
|
|||
QueryInterface(Components.interfaces.nsIFileURL).file;
|
||||
}
|
||||
|
||||
function getTestDataItemUrl(path) {
|
||||
return OS.Path.join("resource://zotero-unit-tests/data", path);
|
||||
function getTestDataUrl(path) {
|
||||
path = path.split('/');
|
||||
if (path[0].length == 0) {
|
||||
path.splice(0, 1);
|
||||
}
|
||||
return "resource://zotero-unit-tests/data/" + path.join('/');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -682,10 +682,10 @@ describe("Zotero.CollectionTreeView", function() {
|
|||
it('should add a translated feed item recovered from an URL', function* (){
|
||||
var feed = yield createFeed();
|
||||
var collection = yield createDataObject('collection', false, { skipSelect: true });
|
||||
var url = getTestDataItemUrl('metadata/journalArticle-single.html');
|
||||
var url = getTestDataUrl('metadata/journalArticle-single.html');
|
||||
var feedItem = yield createDataObject('feedItem', {libraryID: feed.libraryID}, { skipSelect: true });
|
||||
feedItem.setField('url', url);
|
||||
yield feedItem.forceSaveTx();
|
||||
yield feedItem.saveTx();
|
||||
var translateFn = sinon.spy(feedItem, 'translate');
|
||||
|
||||
// Add observer to wait for collection add
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
<description>How do Americans get ready to work with Russians aboard the International Space Station? They take a crash course in culture, language and protocol at Russia's <a href="http://howe.iki.rssi.ru/GCTC/gctc_e.htm">Star City</a>.</description>
|
||||
<pubDate>Tue, 03 Jun 2003 09:39:21 GMT</pubDate>
|
||||
<guid>http://liftoff.msfc.nasa.gov/2003/06/03.html#item573</guid>
|
||||
<enclosure url="http://www.example.com/example.pdf" type="application/pdf" />
|
||||
</item>
|
||||
<item>
|
||||
<title>The Engine That Does More</title>
|
||||
|
|
|
@ -23,11 +23,11 @@ describe("Zotero.FeedItem", function () {
|
|||
it("should accept required fields as arguments", function* () {
|
||||
let guid = Zotero.randomString();
|
||||
let feedItem = new Zotero.FeedItem();
|
||||
yield assert.isRejected(feedItem.forceSaveTx());
|
||||
yield assert.isRejected(feedItem.saveTx());
|
||||
|
||||
feedItem = new Zotero.FeedItem('book', { guid });
|
||||
feedItem.libraryID = libraryID;
|
||||
yield assert.isFulfilled(feedItem.forceSaveTx());
|
||||
yield assert.isFulfilled(feedItem.saveTx());
|
||||
|
||||
assert.equal(feedItem.itemTypeID, Zotero.ItemTypes.getID('book'));
|
||||
assert.equal(feedItem.guid, guid);
|
||||
|
@ -83,7 +83,7 @@ describe("Zotero.FeedItem", function () {
|
|||
expectedTimestamp = Date.now();
|
||||
feedItem.isRead = true;
|
||||
yield Zotero.Promise.delay(2001);
|
||||
yield feedItem.forceSaveTx();
|
||||
yield feedItem.saveTx();
|
||||
|
||||
readTime = yield Zotero.DB.valueQueryAsync('SELECT readTime FROM feedItems WHERE itemID=?', feedItem.id);
|
||||
readTime = Zotero.Date.sqlToDate(readTime, true).getTime();
|
||||
|
@ -107,41 +107,36 @@ describe("Zotero.FeedItem", function () {
|
|||
})
|
||||
});
|
||||
describe("#save()", function() {
|
||||
it("should require edit check override", function* () {
|
||||
let feedItem = new Zotero.FeedItem('book', { guid: Zotero.randomString() });
|
||||
feedItem.libraryID = feed.libraryID;
|
||||
yield assert.isRejected(feedItem.saveTx(), /^Error: Cannot edit feedItem in read-only library/);
|
||||
});
|
||||
it("should require feed being set", function* () {
|
||||
let feedItem = new Zotero.FeedItem('book', { guid: Zotero.randomString() });
|
||||
// Defaults to user library ID
|
||||
yield assert.isRejected(feedItem.forceSaveTx(), /^Error: Cannot add /);
|
||||
yield assert.isRejected(feedItem.saveTx(), /^Error: Cannot add /);
|
||||
});
|
||||
it("should require GUID being set", function* () {
|
||||
let feedItem = new Zotero.FeedItem('book');
|
||||
feedItem.libraryID = feed.libraryID;
|
||||
yield assert.isRejected(feedItem.forceSaveTx(), /^Error: GUID must be set before saving FeedItem$/);
|
||||
yield assert.isRejected(feedItem.saveTx(), /^Error: GUID must be set before saving FeedItem$/);
|
||||
});
|
||||
it("should require a unique GUID", function* () {
|
||||
let guid = Zotero.randomString();
|
||||
let feedItem1 = yield createDataObject('feedItem', { libraryID, guid });
|
||||
|
||||
let feedItem2 = createUnsavedDataObject('feedItem', { libraryID, guid });
|
||||
yield assert.isRejected(feedItem2.forceSaveTx());
|
||||
yield assert.isRejected(feedItem2.saveTx());
|
||||
|
||||
// But we should be able to save it after deleting the original feed
|
||||
yield feedItem1.forceEraseTx();
|
||||
yield assert.isFulfilled(feedItem2.forceSaveTx());
|
||||
yield feedItem1.eraseTx();
|
||||
yield assert.isFulfilled(feedItem2.saveTx());
|
||||
});
|
||||
it("should require item type being set", function* () {
|
||||
let feedItem = new Zotero.FeedItem(null, { guid: Zotero.randomString() });
|
||||
feedItem.libraryID = feed.libraryID;
|
||||
yield assert.isRejected(feedItem.forceSaveTx(), /^Error: Item type must be set before saving$/);
|
||||
yield assert.isRejected(feedItem.saveTx(), /^Error: Item type must be set before saving$/);
|
||||
});
|
||||
it("should save feed item", function* () {
|
||||
let guid = Zotero.randomString();
|
||||
let feedItem = createUnsavedDataObject('feedItem', { libraryID, guid });
|
||||
yield assert.isFulfilled(feedItem.forceSaveTx());
|
||||
yield assert.isFulfilled(feedItem.saveTx());
|
||||
|
||||
feedItem = yield Zotero.FeedItems.getAsync(feedItem.id);
|
||||
assert.ok(feedItem);
|
||||
|
@ -155,7 +150,7 @@ describe("Zotero.FeedItem", function () {
|
|||
let feedItem = new Zotero.FeedItem(null, type, feed.libraryID);
|
||||
feedItem.fromJSON(allTypesAndFields[type]);
|
||||
|
||||
yield feedItem.forceSaveTx();
|
||||
yield feedItem.saveTx();
|
||||
|
||||
feedItems.push(feedItem);
|
||||
}
|
||||
|
@ -172,7 +167,7 @@ describe("Zotero.FeedItem", function () {
|
|||
let feedItem = yield createDataObject('feedItem', { libraryID });
|
||||
|
||||
feedItem.setField('title', 'bar');
|
||||
yield assert.isFulfilled(feedItem.forceSaveTx());
|
||||
yield assert.isFulfilled(feedItem.saveTx());
|
||||
assert.equal(feedItem.getField('title'), 'bar');
|
||||
});
|
||||
});
|
||||
|
@ -180,15 +175,22 @@ describe("Zotero.FeedItem", function () {
|
|||
it("should erase an existing feed item", function* () {
|
||||
let feedItem = yield createDataObject('feedItem', { libraryID });
|
||||
|
||||
yield feedItem.forceEraseTx();
|
||||
yield feedItem.eraseTx();
|
||||
assert.isFalse(yield Zotero.FeedItems.getAsync(feedItem.id));
|
||||
|
||||
//yield assert.isRejected(feedItem.forceEraseTx(), "does not allow erasing twice");
|
||||
//yield assert.isRejected(feedItem.EraseTx(), "does not allow erasing twice");
|
||||
});
|
||||
it("should require edit check override to erase", function* () {
|
||||
let feedItem = yield createDataObject('feedItem', { libraryID });
|
||||
it("should remove synced setting if exists", function* () {
|
||||
let item = yield createDataObject('feedItem', { libraryID });
|
||||
|
||||
yield assert.isRejected(feedItem.eraseTx(), /^Error: Cannot edit feedItem in read-only library/);
|
||||
yield item.toggleRead();
|
||||
let syncedSettings = feed.getSyncedSettings();
|
||||
assert.ok(syncedSettings.markedAsRead[item.guid]);
|
||||
|
||||
yield item.eraseTx();
|
||||
|
||||
syncedSettings = feed.getSyncedSettings();
|
||||
assert.notOk(syncedSettings.markedAsRead[item.guid]);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -196,7 +198,7 @@ describe("Zotero.FeedItem", function () {
|
|||
it('should toggle state', function* () {
|
||||
let item = yield createDataObject('feedItem', { libraryID });
|
||||
item.isRead = false;
|
||||
yield item.forceSaveTx();
|
||||
yield item.saveTx();
|
||||
|
||||
yield item.toggleRead();
|
||||
assert.isTrue(item.isRead, "item is toggled to read state");
|
||||
|
@ -204,7 +206,7 @@ describe("Zotero.FeedItem", function () {
|
|||
it('should save if specified state is different from current', function* (){
|
||||
let item = yield createDataObject('feedItem', { libraryID });
|
||||
item.isRead = false;
|
||||
yield item.forceSaveTx();
|
||||
yield item.saveTx();
|
||||
sinon.spy(item, 'save');
|
||||
|
||||
yield item.toggleRead(true);
|
||||
|
@ -215,6 +217,17 @@ describe("Zotero.FeedItem", function () {
|
|||
yield item.toggleRead(true);
|
||||
assert.isFalse(item.save.called, "item was not saved on toggle read to same state");
|
||||
});
|
||||
it('should set relevant synced settings', function* () {
|
||||
let item = yield createDataObject('feedItem', { libraryID });
|
||||
item.isRead = false;
|
||||
yield item.saveTx();
|
||||
|
||||
yield item.toggleRead();
|
||||
|
||||
let feed = Zotero.Feeds.get(item.libraryID);
|
||||
let syncedSettings = feed.getSyncedSettings();
|
||||
assert.ok(syncedSettings.markedAsRead[item.guid], "item marked as read stored in synced settings");
|
||||
});
|
||||
});
|
||||
|
||||
describe('#translate()', function() {
|
||||
|
@ -224,9 +237,9 @@ describe("Zotero.FeedItem", function () {
|
|||
});
|
||||
it('translates and saves items', function* () {
|
||||
var feedItem = yield createDataObject('feedItem', {libraryID});
|
||||
var url = getTestDataItemUrl('metadata/journalArticle-single.html');
|
||||
var url = getTestDataUrl('metadata/journalArticle-single.html');
|
||||
feedItem.setField('url', url);
|
||||
yield feedItem.forceSaveTx();
|
||||
yield feedItem.saveTx();
|
||||
|
||||
yield feedItem.translate();
|
||||
|
||||
|
@ -237,9 +250,9 @@ describe("Zotero.FeedItem", function () {
|
|||
let collection = yield createDataObject('collection', {libraryID: group.libraryID});
|
||||
|
||||
var feedItem = yield createDataObject('feedItem', {libraryID});
|
||||
var url = getTestDataItemUrl('metadata/journalArticle-single.html');
|
||||
var url = getTestDataUrl('metadata/journalArticle-single.html');
|
||||
feedItem.setField('url', url);
|
||||
yield feedItem.forceSaveTx();
|
||||
yield feedItem.saveTx();
|
||||
|
||||
yield feedItem.translate(group.libraryID, collection.id);
|
||||
|
||||
|
|
|
@ -7,6 +7,51 @@ describe("Zotero.FeedItems", function () {
|
|||
return clearFeeds();
|
||||
});
|
||||
|
||||
describe("#getMarkedAsRead", function() {
|
||||
var items = [];
|
||||
var result;
|
||||
before(function* () {
|
||||
for (let i = 0; i < 4; i++) {
|
||||
let f = yield createDataObject('feedItem', {libraryID: feed.libraryID, guid: 'http://www.example.com/' + i});
|
||||
items.push(f);
|
||||
}
|
||||
yield items[0].toggleRead();
|
||||
yield items[2].toggleRead();
|
||||
result = yield Zotero.FeedItems.getMarkedAsRead(feed.libraryID);
|
||||
});
|
||||
it('should get all marked as read items', function() {
|
||||
assert.include(result, items[0]);
|
||||
assert.include(result, items[2]);
|
||||
});
|
||||
it('should not include items that were not marked', function() {
|
||||
assert.notInclude(result, items[1]);
|
||||
assert.notInclude(result, items[3]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#markAsReadByGUID", function() {
|
||||
var items = [];
|
||||
var result;
|
||||
before(function* () {
|
||||
for (let i = 0; i < 4; i++) {
|
||||
let f = yield createDataObject('feedItem', {
|
||||
libraryID: feed.libraryID,
|
||||
guid: 'http://' + Zotero.Utilities.randomString() + '.com/feed.rss'
|
||||
});
|
||||
items.push(f);
|
||||
}
|
||||
yield Zotero.FeedItems.markAsReadByGUID([items[0].guid, items[2].guid]);
|
||||
});
|
||||
it('should mark as read only specified guids', function() {
|
||||
assert.isTrue(items[0].isRead);
|
||||
assert.isTrue(items[2].isRead);
|
||||
});
|
||||
it('should leave other items marked unread', function() {
|
||||
assert.isFalse(items[1].isRead);
|
||||
assert.isFalse(items[3].isRead);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#getIDFromGUID()", function() {
|
||||
it("should return false for non-existent GUID", function* () {
|
||||
let id = yield Zotero.FeedItems.getIDFromGUID(Zotero.randomString());
|
||||
|
@ -14,7 +59,7 @@ describe("Zotero.FeedItems", function () {
|
|||
});
|
||||
it("should return feed item id from GUID", function* () {
|
||||
let feedItem = yield createDataObject('feedItem', { libraryID: feed.libraryID });
|
||||
yield feedItem.forceSaveTx();
|
||||
yield feedItem.saveTx();
|
||||
|
||||
let id2 = yield Zotero.FeedItems.getIDFromGUID(feedItem.guid);
|
||||
assert.equal(id2, feedItem.id);
|
||||
|
@ -24,7 +69,7 @@ describe("Zotero.FeedItems", function () {
|
|||
it("should return feed item from GUID", function* () {
|
||||
let guid = Zotero.randomString();
|
||||
let feedItem = yield createDataObject('feedItem', { guid, libraryID: feed.libraryID });
|
||||
yield feedItem.forceSaveTx();
|
||||
yield feedItem.saveTx();
|
||||
|
||||
let feedItem2 = yield Zotero.FeedItems.getAsyncByGUID(guid);
|
||||
assert.equal(feedItem2.id, feedItem.id);
|
||||
|
@ -48,7 +93,7 @@ describe("Zotero.FeedItems", function () {
|
|||
for (let i = 0; i < 10; i++) {
|
||||
let item = yield createDataObject('feedItem', { guid: Zotero.randomString(), libraryID: feed.id });
|
||||
item.isRead = true;
|
||||
yield item.forceSaveTx();
|
||||
yield item.saveTx();
|
||||
items.push(item);
|
||||
}
|
||||
ids = Array.map(items, (i) => i.id);
|
||||
|
@ -66,7 +111,7 @@ describe("Zotero.FeedItems", function () {
|
|||
|
||||
it('should toggle all items read if at least one unread', function* () {
|
||||
items[0].isRead = false;
|
||||
yield items[0].forceSaveTx();
|
||||
yield items[0].saveTx();
|
||||
|
||||
yield Zotero.FeedItems.toggleReadByID(ids);
|
||||
|
||||
|
@ -85,7 +130,7 @@ describe("Zotero.FeedItems", function () {
|
|||
|
||||
it('should toggle all items unread if unread state specified', function* () {
|
||||
items[0].isRead = false;
|
||||
yield items[0].forceSaveTx();
|
||||
yield items[0].saveTx();
|
||||
|
||||
yield Zotero.FeedItems.toggleReadByID(ids, false);
|
||||
|
||||
|
@ -93,5 +138,15 @@ describe("Zotero.FeedItems", function () {
|
|||
assert.isFalse(save.thisValues[i].isRead, "#toggleRead called with true");
|
||||
}
|
||||
});
|
||||
|
||||
it('should set relevant sync settings', function* () {
|
||||
items[0].isRead = false;
|
||||
yield items[0].saveTx();
|
||||
yield Zotero.FeedItems.toggleReadByID(ids);
|
||||
|
||||
let syncedFeeds = Zotero.SyncedSettings.get(Zotero.Libraries.userLibraryID, 'feeds');
|
||||
let markedAsRead = Object.keys(syncedFeeds[feed.url].markedAsRead);
|
||||
assert.deepEqual(markedAsRead, Object.keys(items).map((k) => items[k].guid));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
describe("Zotero.FeedReader", function () {
|
||||
|
||||
var htmlUrl = getTestDataItemUrl("test.html");
|
||||
var htmlUrl = getTestDataUrl("test.html");
|
||||
|
||||
var feedUrl = getTestDataItemUrl("feed.rss");
|
||||
var feedUrl = getTestDataUrl("feed.rss");
|
||||
var feedInfo = {
|
||||
title: 'Liftoff News',
|
||||
subtitle: 'Liftoff to Space Exploration.',
|
||||
|
@ -18,7 +18,7 @@ describe("Zotero.FeedReader", function () {
|
|||
language: 'en-us'
|
||||
};
|
||||
|
||||
var detailedFeedUrl = getTestDataItemUrl("feedDetailed.rss");
|
||||
var detailedFeedUrl = getTestDataUrl("feedDetailed.rss");
|
||||
var detailedFeedInfo = {
|
||||
title: 'Feed',
|
||||
subtitle: 'Feed Description',
|
||||
|
@ -106,20 +106,15 @@ describe("Zotero.FeedReader", function () {
|
|||
});
|
||||
|
||||
it('should parse items correctly for a sparse feed', function* () {
|
||||
let expected = {
|
||||
guid: 'http://liftoff.msfc.nasa.gov/2003/06/03.html#item573',
|
||||
let expected = { guid: 'http://liftoff.msfc.nasa.gov/2003/06/03.html#item573',
|
||||
title: 'Star City',
|
||||
abstractNote: 'How do Americans get ready to work with Russians aboard the International Space Station? They take a crash course in culture, language and protocol at Russia\'s Star City.',
|
||||
url: 'http://liftoff.msfc.nasa.gov/news/2003/news-starcity.asp',
|
||||
creators: [{
|
||||
firstName: '',
|
||||
lastName: 'editor@example.com',
|
||||
creatorType: 'author',
|
||||
fieldMode: 1
|
||||
}],
|
||||
creators: [{ firstName: '', lastName: 'editor@example.com', creatorType: 'author', fieldMode: 1 }],
|
||||
date: 'Tue, 03 Jun 2003 09:39:21 GMT',
|
||||
language: 'en-us',
|
||||
itemType: 'journalArticle'
|
||||
itemType: 'journalArticle',
|
||||
enclosedItems: [{ url: 'http://www.example.com/example.pdf', contentType: 'application/pdf' }]
|
||||
};
|
||||
|
||||
let fr = new Zotero.FeedReader(feedUrl);
|
||||
|
@ -149,7 +144,8 @@ describe("Zotero.FeedReader", function () {
|
|||
publisher: 'Publisher',
|
||||
rights: '©2016 Published by Publisher',
|
||||
language: 'en',
|
||||
itemType: 'journalArticle'
|
||||
itemType: 'journalArticle',
|
||||
enclosedItems: []
|
||||
};
|
||||
|
||||
let fr = new Zotero.FeedReader(detailedFeedUrl);
|
||||
|
|
|
@ -42,12 +42,11 @@ describe("Zotero.Feed", function() {
|
|||
yield feed.saveTx();
|
||||
assert.isFalse(feed.editable);
|
||||
});
|
||||
it("should not allow adding items without editCheck override", function* () {
|
||||
it("should allow adding items without editCheck override", function* () {
|
||||
let feed = yield createFeed();
|
||||
let feedItem = new Zotero.FeedItem('book', { guid: Zotero.randomString() });
|
||||
feedItem.libraryID = feed.libraryID;
|
||||
yield assert.isRejected(feedItem.saveTx(), /^Error: Cannot edit feedItem in read-only library/);
|
||||
yield assert.isFulfilled(feedItem.saveTx({ skipEditCheck: true }));
|
||||
yield assert.isFulfilled(feedItem.saveTx());
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -135,6 +134,31 @@ describe("Zotero.Feed", function() {
|
|||
assert.equal(feed.name, 'bar');
|
||||
assert.equal(dbVal, feed.name);
|
||||
});
|
||||
it("should add a new synced setting after creation", function* () {
|
||||
let url = 'http://' + Zotero.Utilities.randomString(10, 'abcde') + '.com/feed.rss';
|
||||
|
||||
let syncedFeeds = Zotero.SyncedSettings.get(Zotero.Libraries.userLibraryID, 'feeds');
|
||||
assert.notOk(syncedFeeds[url]);
|
||||
|
||||
yield createFeed({url});
|
||||
|
||||
syncedFeeds = Zotero.SyncedSettings.get(Zotero.Libraries.userLibraryID, 'feeds');
|
||||
assert.ok(syncedFeeds[url]);
|
||||
});
|
||||
it("should remove previous feed and add a new one if url changed", function* () {
|
||||
let feed = yield createFeed();
|
||||
|
||||
let syncedFeeds = Zotero.SyncedSettings.get(Zotero.Libraries.userLibraryID, 'feeds');
|
||||
assert.ok(syncedFeeds[feed.url]);
|
||||
|
||||
let oldUrl = feed.url;
|
||||
feed.url = 'http://' + Zotero.Utilities.randomString(10, 'abcde') + '.com/feed.rss';
|
||||
yield feed.saveTx();
|
||||
|
||||
syncedFeeds = Zotero.SyncedSettings.get(Zotero.Libraries.userLibraryID, 'feeds');
|
||||
assert.notOk(syncedFeeds[oldUrl]);
|
||||
assert.ok(syncedFeeds[feed.url]);
|
||||
});
|
||||
});
|
||||
describe("#erase()", function() {
|
||||
it("should erase a saved feed", function* () {
|
||||
|
@ -160,10 +184,59 @@ describe("Zotero.Feed", function() {
|
|||
|
||||
assert.notOk(yield Zotero.FeedItems.getAsync(feedItem.id));
|
||||
});
|
||||
it("should remove synced settings", function* () {
|
||||
let url = 'http://' + Zotero.Utilities.randomString(10, 'abcde') + '.com/feed.rss';
|
||||
let feed = yield createFeed({url});
|
||||
|
||||
let syncedFeeds = Zotero.SyncedSettings.get(Zotero.Libraries.userLibraryID, 'feeds');
|
||||
assert.ok(syncedFeeds[feed.url]);
|
||||
|
||||
yield feed.eraseTx();
|
||||
|
||||
syncedFeeds = Zotero.SyncedSettings.get(Zotero.Libraries.userLibraryID, 'feeds');
|
||||
assert.notOk(syncedFeeds[url]);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe("#getSyncedSettings", function() {
|
||||
it("should return correct synced settings for the feed", function* () {
|
||||
let url = 'http://' + Zotero.Utilities.randomString(10, 'abcde') + '.com/feed.rss';
|
||||
let syncedFeeds = Zotero.SyncedSettings.get(Zotero.Libraries.userLibraryID, 'feeds');
|
||||
assert.notOk(syncedFeeds[url]);
|
||||
|
||||
let feed = yield createFeed({url});
|
||||
|
||||
syncedFeeds = Zotero.SyncedSettings.get(Zotero.Libraries.userLibraryID, 'feeds');
|
||||
assert.ok(syncedFeeds[url]);
|
||||
|
||||
let syncedData = feed.getSyncedSettings();
|
||||
|
||||
assert.deepEqual(syncedData, syncedFeeds[url]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#storeSyncedSettings", function() {
|
||||
it("should store updated settings for the feed", function* () {
|
||||
let guid = Zotero.Utilities.randomString();
|
||||
let feed = yield createFeed();
|
||||
|
||||
let syncedFeeds = Zotero.SyncedSettings.get(Zotero.Libraries.userLibraryID, 'feeds');
|
||||
assert.notOk(syncedFeeds[feed.url].markedAsRead[guid]);
|
||||
|
||||
let syncedData = feed.getSyncedSettings();
|
||||
syncedData.markedAsRead[guid] = true;
|
||||
yield feed.setSyncedSettings(syncedData);
|
||||
yield feed.storeSyncedSettings();
|
||||
|
||||
syncedFeeds = Zotero.SyncedSettings.get(Zotero.Libraries.userLibraryID, 'feeds');
|
||||
|
||||
assert.isTrue(syncedFeeds[feed.url].markedAsRead[guid]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("#clearExpiredItems()", function() {
|
||||
var feed, expiredFeedItem, readFeedItem, feedItem, feedItemIDs;
|
||||
var feed, expiredFeedItem, readFeedItem, feedItem, readStillInFeed, feedItemIDs;
|
||||
|
||||
before(function* (){
|
||||
feed = yield createFeed({cleanupAfter: 1});
|
||||
|
@ -173,11 +246,18 @@ describe("Zotero.Feed", function() {
|
|||
expiredFeedItem.isRead = true;
|
||||
expiredFeedItem._feedItemReadTime = Zotero.Date.dateToSQL(
|
||||
new Date(Date.now() - 2 * 24*60*60*1000), true);
|
||||
yield expiredFeedItem.forceSaveTx();
|
||||
yield expiredFeedItem.saveTx();
|
||||
|
||||
readStillInFeed = yield createDataObject('feedItem', { libraryID: feed.libraryID });
|
||||
// Read 2 days ago
|
||||
readStillInFeed.isRead = true;
|
||||
readStillInFeed._feedItemReadTime = Zotero.Date.dateToSQL(
|
||||
new Date(Date.now() - 2 * 24*60*60*1000), true);
|
||||
yield readStillInFeed.saveTx();
|
||||
|
||||
readFeedItem = yield createDataObject('feedItem', { libraryID: feed.libraryID });
|
||||
readFeedItem.isRead = true;
|
||||
yield readFeedItem.forceSaveTx();
|
||||
yield readFeedItem.saveTx();
|
||||
|
||||
feedItem = yield createDataObject('feedItem', { libraryID: feed.libraryID });
|
||||
|
||||
|
@ -186,8 +266,9 @@ describe("Zotero.Feed", function() {
|
|||
assert.include(feedItemIDs, feedItem.id, "feed contains unread feed item");
|
||||
assert.include(feedItemIDs, readFeedItem.id, "feed contains read feed item");
|
||||
assert.include(feedItemIDs, expiredFeedItem.id, "feed contains expired feed item");
|
||||
assert.include(feedItemIDs, readStillInFeed.id, "feed contains expired but still in rss feed item");
|
||||
|
||||
yield feed.clearExpiredItems();
|
||||
yield feed.clearExpiredItems(new Set([readStillInFeed.id]));
|
||||
|
||||
feedItemIDs = yield Zotero.FeedItems.getAll(feed.libraryID).map((row) => row.id);
|
||||
});
|
||||
|
@ -198,7 +279,11 @@ describe("Zotero.Feed", function() {
|
|||
|
||||
it('should not clear read items that have not expired yet', function() {
|
||||
assert.include(feedItemIDs, readFeedItem.id, "feed still contains new feed item");
|
||||
})
|
||||
});
|
||||
|
||||
it('should not clear read items that are still in rss', function() {
|
||||
assert.include(feedItemIDs, readStillInFeed.id, "feed still contains read still in rss feed item");
|
||||
});
|
||||
|
||||
it('should not clear unread items', function() {
|
||||
assert.include(feedItemIDs, feedItem.id, "feed still contains new feed item");
|
||||
|
@ -207,8 +292,8 @@ describe("Zotero.Feed", function() {
|
|||
|
||||
describe('#updateFeed()', function() {
|
||||
var feed;
|
||||
var feedUrl = getTestDataItemUrl("feed.rss");
|
||||
var modifiedFeedUrl = getTestDataItemUrl("feedModified.rss");
|
||||
var feedUrl = getTestDataUrl("feed.rss");
|
||||
var modifiedFeedUrl = getTestDataUrl("feedModified.rss");
|
||||
|
||||
beforeEach(function* (){
|
||||
feed = yield createFeed();
|
||||
|
@ -232,11 +317,12 @@ describe("Zotero.Feed", function() {
|
|||
});
|
||||
|
||||
it('should add new feed items', function* () {
|
||||
let feedItems = yield Zotero.FeedItems.getAll(feed.id);
|
||||
let feedItems = yield Zotero.FeedItems.getAll(feed.id, true);
|
||||
assert.equal(feedItems.length, 3);
|
||||
});
|
||||
|
||||
it('should set lastCheck, lastUpdated and lastGUID values', function* () {
|
||||
it('should set lastCheck and lastUpdated values', function* () {
|
||||
yield clearFeeds();
|
||||
let feed = yield createFeed();
|
||||
feed._feedUrl = feedUrl;
|
||||
|
||||
|
@ -245,15 +331,14 @@ describe("Zotero.Feed", function() {
|
|||
|
||||
yield feed.updateFeed();
|
||||
|
||||
assert.ok(feed.lastCheck >= Zotero.Date.dateToSQL(new Date(Date.now() - 1000*60), true));
|
||||
assert.ok(feed.lastUpdate >= Zotero.Date.dateToSQL(new Date(Date.now() - 1000*60), true));
|
||||
assert.equal(feed.lastGUID, 'http://liftoff.msfc.nasa.gov/2003/06/03.html#item573:'+feed.id);
|
||||
assert.isTrue(feed.lastCheck > Zotero.Date.dateToSQL(new Date(Date.now() - 1000*60), true), 'feed.lastCheck updated');
|
||||
assert.isTrue(feed.lastUpdate > Zotero.Date.dateToSQL(new Date(Date.now() - 1000*60), true), 'feed.lastUpdate updated');
|
||||
});
|
||||
it('should update modified items and set unread', function* () {
|
||||
let feedItem = yield Zotero.FeedItems.getAsyncByGUID("http://liftoff.msfc.nasa.gov/2003/06/03.html#item573:"+feed.id);
|
||||
let feedItem = yield Zotero.FeedItems.getAsyncByGUID("http://liftoff.msfc.nasa.gov/2003/06/03.html#item573");
|
||||
feedItem.isRead = true;
|
||||
yield feedItem.forceSaveTx();
|
||||
feedItem = yield Zotero.FeedItems.getAsyncByGUID("http://liftoff.msfc.nasa.gov/2003/06/03.html#item573:"+feed.id);
|
||||
yield feedItem.saveTx();
|
||||
feedItem = yield Zotero.FeedItems.getAsyncByGUID("http://liftoff.msfc.nasa.gov/2003/06/03.html#item573");
|
||||
assert.isTrue(feedItem.isRead);
|
||||
|
||||
let oldDateModified = feedItem.getField('date');
|
||||
|
@ -261,7 +346,7 @@ describe("Zotero.Feed", function() {
|
|||
feed._feedUrl = modifiedFeedUrl;
|
||||
yield feed.updateFeed();
|
||||
|
||||
feedItem = yield Zotero.FeedItems.getAsyncByGUID("http://liftoff.msfc.nasa.gov/2003/06/03.html#item573:"+feed.id);
|
||||
feedItem = yield Zotero.FeedItems.getAsyncByGUID("http://liftoff.msfc.nasa.gov/2003/06/03.html#item573");
|
||||
|
||||
assert.notEqual(oldDateModified, feedItem.getField('date'));
|
||||
assert.isFalse(feedItem.isRead)
|
||||
|
@ -272,7 +357,7 @@ describe("Zotero.Feed", function() {
|
|||
feed._feedUrl = modifiedFeedUrl;
|
||||
yield feed.updateFeed();
|
||||
|
||||
assert.equal(save.thisValues[0].guid, "http://liftoff.msfc.nasa.gov/2003/06/03.html#item573:"+feed.id);
|
||||
assert.equal(save.thisValues[0].guid, "http://liftoff.msfc.nasa.gov/2003/06/03.html#item573");
|
||||
save.restore();
|
||||
});
|
||||
it('should update unread count', function* () {
|
||||
|
@ -281,7 +366,7 @@ describe("Zotero.Feed", function() {
|
|||
let feedItems = yield Zotero.FeedItems.getAll(feed.id);
|
||||
for (let feedItem of feedItems) {
|
||||
feedItem.isRead = true;
|
||||
yield feedItem.forceSaveTx();
|
||||
yield feedItem.saveTx();
|
||||
}
|
||||
|
||||
feed._feedUrl = modifiedFeedUrl;
|
||||
|
@ -289,20 +374,11 @@ describe("Zotero.Feed", function() {
|
|||
|
||||
assert.equal(feed.unreadCount, 2);
|
||||
});
|
||||
it('should not re-add deleted items, but add new ones', function* () {
|
||||
let feedItem = yield Zotero.FeedItems.getAsyncByGUID("http://liftoff.msfc.nasa.gov/2003/05/20.html#item570:"+feed.id);
|
||||
yield feedItem.forceEraseTx();
|
||||
it('should add a link to enclosed pdfs from <enclosure/> elements', function* () {
|
||||
let feedItem = yield Zotero.FeedItems.getAsyncByGUID("http://liftoff.msfc.nasa.gov/2003/06/03.html#item573");
|
||||
let pdf = yield Zotero.Items.getAsync(feedItem.getAttachments()[0]);
|
||||
|
||||
let feedItems = yield Zotero.FeedItems.getAll(feed.id);
|
||||
for (let feedItem of feedItems) {
|
||||
feedItem.isRead = true;
|
||||
yield feedItem.forceSaveTx();
|
||||
}
|
||||
|
||||
feed._feedUrl = modifiedFeedUrl;
|
||||
yield feed.updateFeed();
|
||||
|
||||
assert.equal(feed.unreadCount, 2);
|
||||
assert.equal(pdf.getField('url'), "http://www.example.com/example.pdf");
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -311,11 +387,6 @@ describe("Zotero.Feed", function() {
|
|||
before(function* () {
|
||||
feed = yield createFeed();
|
||||
})
|
||||
it("should not allow adding regular items", function* () {
|
||||
let item = new Zotero.Item('book');
|
||||
item.libraryID = feed.libraryID;
|
||||
yield assert.isRejected(item.saveTx({ skipEditCheck: true }), /^Error: Cannot add /);
|
||||
});
|
||||
it("should not allow adding collections", function* () {
|
||||
let collection = new Zotero.Collection({ name: 'test', libraryID: feed.libraryID });
|
||||
yield assert.isRejected(collection.saveTx({ skipEditCheck: true }), /^Error: Cannot add /);
|
||||
|
@ -327,7 +398,7 @@ describe("Zotero.Feed", function() {
|
|||
it("should allow adding feed item", function* () {
|
||||
let feedItem = new Zotero.FeedItem('book', { guid: Zotero.randomString() });
|
||||
feedItem.libraryID = feed.libraryID;
|
||||
yield assert.isFulfilled(feedItem.forceSaveTx());
|
||||
yield assert.isFulfilled(feedItem.saveTx());
|
||||
});
|
||||
});
|
||||
})
|
||||
|
|
|
@ -3,6 +3,77 @@ describe("Zotero.Feeds", function () {
|
|||
after(function* () {
|
||||
yield clearFeeds();
|
||||
});
|
||||
|
||||
describe("#restoreFromJSON", function() {
|
||||
var json = {};
|
||||
var expiredFeedURL, existingFeedURL;
|
||||
|
||||
before(function() {
|
||||
sinon.stub(Zotero.Feed.prototype, 'updateFeed').resolves();
|
||||
});
|
||||
|
||||
after(function() {
|
||||
Zotero.Feed.prototype.updateFeed.restore();
|
||||
});
|
||||
|
||||
beforeEach(function* () {
|
||||
yield clearFeeds();
|
||||
|
||||
for (let i = 0; i < 2; i++) {
|
||||
let url = "http://" + Zotero.Utilities.randomString(10, 'abcdefgh') + ".com/feed.rss";
|
||||
json[url] = {
|
||||
url,
|
||||
name: Zotero.Utilities.randomString(),
|
||||
refreshInterval: 5,
|
||||
cleanupAfter: 3,
|
||||
markedAsRead: []
|
||||
};
|
||||
if (i == 0) {
|
||||
existingFeedURL = url;
|
||||
yield createFeed({url});
|
||||
}
|
||||
}
|
||||
expiredFeedURL = (yield createFeed()).url;
|
||||
});
|
||||
|
||||
it("restores correctly when merge is true", function* () {
|
||||
let feeds = Zotero.Feeds.getAll();
|
||||
assert.equal(feeds.length, 2);
|
||||
|
||||
yield Zotero.Feeds.restoreFromJSON(json, true);
|
||||
feeds = Zotero.Feeds.getAll();
|
||||
|
||||
for (let url in json) {
|
||||
let feed = Zotero.Feeds.getByURL(url);
|
||||
assert.ok(feed, "new feed created");
|
||||
}
|
||||
|
||||
let expiredFeed = Zotero.Feeds.getByURL(expiredFeedURL);
|
||||
assert.ok(expiredFeed, "does not remove feeds not in JSON");
|
||||
|
||||
let existingFeed = Zotero.Feeds.getByURL(existingFeedURL);
|
||||
assert.ok(existingFeed, "does not remove feeds in database and JSON");
|
||||
});
|
||||
|
||||
it("restores correctly when merge is false", function* () {
|
||||
let feeds = Zotero.Feeds.getAll();
|
||||
assert.equal(feeds.length, 2);
|
||||
|
||||
yield Zotero.Feeds.restoreFromJSON(json);
|
||||
feeds = Zotero.Feeds.getAll();
|
||||
|
||||
for (let url in json) {
|
||||
let feed = Zotero.Feeds.getByURL(url);
|
||||
assert.ok(feed, "new feed created");
|
||||
}
|
||||
|
||||
let expiredFeed = Zotero.Feeds.getByURL(expiredFeedURL);
|
||||
assert.notOk(expiredFeed, "removes feeds not in JSON");
|
||||
|
||||
let existingFeed = Zotero.Feeds.getByURL(existingFeedURL);
|
||||
assert.ok(existingFeed, "does not remove feeds in database and JSON");
|
||||
});
|
||||
});
|
||||
|
||||
describe("#haveFeeds()", function() {
|
||||
it("should return false for a DB without feeds", function* () {
|
||||
|
@ -40,6 +111,23 @@ describe("Zotero.Feeds", function () {
|
|||
assert.sameMembers(feeds, [feed1, feed2]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getByURL', function() {
|
||||
it("should return a feed by url", function* () {
|
||||
let url = 'http://' + Zotero.Utilities.randomString(10, 'abcdefg') + '.com/feed.rss';
|
||||
yield createFeed({url});
|
||||
let feed = Zotero.Feeds.getByURL(url);
|
||||
assert.ok(feed);
|
||||
assert.equal(feed.url, url);
|
||||
});
|
||||
it("should return undefined if feed does not exist", function* () {
|
||||
var feed;
|
||||
assert.doesNotThrow(function() {
|
||||
feed = Zotero.Feeds.getByURL('doesnotexist');
|
||||
});
|
||||
assert.isUndefined(feed);
|
||||
});
|
||||
});
|
||||
describe('#updateFeeds', function() {
|
||||
var freshFeed, recentFeed, oldFeed;
|
||||
var _updateFeed;
|
||||
|
@ -49,7 +137,7 @@ describe("Zotero.Feeds", function () {
|
|||
|
||||
sinon.stub(Zotero.Feeds, 'scheduleNextFeedCheck');
|
||||
_updateFeed = sinon.stub(Zotero.Feed.prototype, '_updateFeed').resolves();
|
||||
let url = getTestDataItemUrl("feed.rss");
|
||||
let url = getTestDataUrl("feed.rss");
|
||||
|
||||
freshFeed = yield createFeed({refreshInterval: 2});
|
||||
freshFeed._feedUrl = url;
|
||||
|
@ -97,7 +185,7 @@ describe("Zotero.Feeds", function () {
|
|||
break;
|
||||
}
|
||||
// should never reach
|
||||
assert.isOk(null, "does not update feed that did not need updating")
|
||||
assert.ok(null, "does not update feed that did not need updating")
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -131,7 +131,7 @@ describe("Zotero.Items", function () {
|
|||
|
||||
let feedItem = new Zotero.FeedItem('journalArticle', { guid: Zotero.randomString() });
|
||||
feedItem.libraryID = feed.libraryID;
|
||||
let id = yield feedItem.forceSaveTx();
|
||||
let id = yield feedItem.saveTx();
|
||||
|
||||
feedItem = yield Zotero.Items.getAsync(id);
|
||||
|
||||
|
|
Loading…
Reference in a new issue