diff --git a/chrome/content/zotero/itemPane.js b/chrome/content/zotero/itemPane.js
index 0ecdb3322..c90a0b966 100644
--- a/chrome/content/zotero/itemPane.js
+++ b/chrome/content/zotero/itemPane.js
@@ -70,7 +70,7 @@ var ZoteroItemPane = new function() {
/*
* Loads an item
*/
- function viewItem(thisItem) {
+ function viewItem(thisItem, mode) {
//Zotero.debug('Viewing item');
// Force blur() when clicking off a textbox to another item in middle
@@ -100,11 +100,11 @@ var ZoteroItemPane = new function() {
_itemBeingEdited = thisItem;
_loaded = {};
- loadPane(_tabs.selectedIndex);
+ loadPane(_tabs.selectedIndex, mode);
}
- function loadPane(index) {
+ function loadPane(index, mode) {
//Zotero.debug('Loading item pane ' + index);
// Clear the tab index when switching panes
@@ -121,7 +121,14 @@ var ZoteroItemPane = new function() {
// Info pane
if (index == 0) {
var itembox = document.getElementById('zotero-editpane-item-box');
- itembox.mode = 'edit';
+ // Hack to allow read-only mode in right pane -- probably a better
+ // way to allow access to this
+ if (mode) {
+ itembox.mode = mode;
+ }
+ else {
+ itembox.mode = 'edit';
+ }
itembox.item = _itemBeingEdited;
}
diff --git a/chrome/content/zotero/overlay.js b/chrome/content/zotero/overlay.js
index 9184bca61..54bbf0776 100644
--- a/chrome/content/zotero/overlay.js
+++ b/chrome/content/zotero/overlay.js
@@ -788,7 +788,12 @@ var ZoteroPane = new function()
if(item.ref.isNote())
{
var noteEditor = document.getElementById('zotero-note-editor');
- noteEditor.mode = 'edit';
+ if (this.itemsView.readOnly) {
+ noteEditor.mode = 'view';
+ }
+ else {
+ noteEditor.mode = 'edit';
+ }
// If loading new or different note, disable undo while we repopulate the text field
// so Undo doesn't end up clearing the field. This also ensures that Undo doesn't
@@ -953,7 +958,7 @@ var ZoteroPane = new function()
}
else
{
- ZoteroItemPane.viewItem(item.ref);
+ ZoteroItemPane.viewItem(item.ref, this.itemsView.readOnly ? 'view' : false);
document.getElementById('zotero-item-pane-content').selectedIndex = 1;
}
}
@@ -1056,7 +1061,8 @@ var ZoteroPane = new function()
var noPrompt = true;
}
// Do nothing in search view
- else if (this.itemsView._itemGroup.isSearch()) {
+ else if (this.itemsView._itemGroup.isSearch() ||
+ this.itemsView._itemGroup.isShare()) {
return;
}
}
@@ -1465,7 +1471,14 @@ var ZoteroPane = new function()
var enable = [], disable = [], show = [], hide = [], multiple = '';
- if (this.itemsView && this.itemsView.selection.count > 0) {
+ // TODO: implement menu for remote items
+ if (this.itemsView.readOnly) {
+ for each(var pos in m) {
+ disable.push(pos);
+ }
+ }
+
+ else if (this.itemsView && this.itemsView.selection.count > 0) {
enable.push(m.showInLibrary, m.addNote, m.attachSnapshot, m.attachLink,
m.sep2, m.duplicateItem, m.deleteItem, m.deleteFromLibrary,
m.exportItems, m.createBib, m.loadReport);
@@ -1607,6 +1620,10 @@ var ZoteroPane = new function()
}
}
else if (tree.id == 'zotero-items-tree') {
+ if (this.itemsView.readOnly) {
+ return;
+ }
+
if (this.itemsView && this.itemsView.selection.currentIndex > -1) {
var item = this.getSelectedItems()[0];
if (item && item.isNote()) {
diff --git a/chrome/content/zotero/overlay.xul b/chrome/content/zotero/overlay.xul
index 42dd8c450..aa7ee0479 100644
--- a/chrome/content/zotero/overlay.xul
+++ b/chrome/content/zotero/overlay.xul
@@ -124,6 +124,8 @@
+
diff --git a/chrome/content/zotero/xpcom/collectionTreeView.js b/chrome/content/zotero/xpcom/collectionTreeView.js
index 0deb40b3a..07c4a620a 100644
--- a/chrome/content/zotero/xpcom/collectionTreeView.js
+++ b/chrome/content/zotero/xpcom/collectionTreeView.js
@@ -36,7 +36,7 @@ Zotero.CollectionTreeView = function()
this._treebox = null;
this.itemToSelect = null;
this._highlightedRows = {};
- this._unregisterID = Zotero.Notifier.registerObserver(this, ['collection', 'search']);
+ this._unregisterID = Zotero.Notifier.registerObserver(this, ['collection', 'search', 'share']);
}
/*
@@ -107,6 +107,13 @@ Zotero.CollectionTreeView.prototype.refresh = function()
}
}
+ var shares = Zotero.Zeroconf.instances;
+ if (shares) {
+ for each(var share in shares) {
+ this._showItem(new Zotero.ItemGroup('share', share), 0, this._dataItems.length); //itemgroup ref, level, beforeRow
+ }
+ }
+
this._refreshHashMap();
// Update the treebox's row count
@@ -162,8 +169,16 @@ Zotero.CollectionTreeView.prototype.notify = function(action, type, ids)
var madeChanges = false;
- if(action == 'delete')
- {
+ if (action == 'refresh') {
+ switch (type) {
+ case 'share':
+ this.reload();
+ this.rememberSelection(savedSelection);
+ break;
+ }
+ }
+
+ else if(action == 'delete') {
//Since a delete involves shifting of rows, we have to do it in order
//sort the ids by row
@@ -672,6 +687,16 @@ Zotero.CollectionTreeView.prototype.canDrop = function(row, orient)
}
return false;
}
+ else if (dataType == 'zotero/item-xml') {
+ var xml = new XML(data.data);
+ for each(var xmlNode in xml.items.item) {
+ var item = Zotero.Sync.Server.Data.xmlToItem(xmlNode);
+ if (item.isRegularItem() || !item.getSource()) {
+ return true;
+ }
+ }
+ return false;
+ }
else if (dataType == 'text/x-moz-url'
|| dataType == 'application/x-moz-file') {
if (this._getItemAtRow(row).isSearch()) {
@@ -733,6 +758,25 @@ Zotero.CollectionTreeView.prototype.drop = function(row, orient)
this._getItemAtRow(row).ref.addItems(toAdd);
}
}
+ else if (dataType == 'zotero/item-xml') {
+ Zotero.DB.beginTransaction();
+ var xml = new XML(data.data);
+ var toAdd = [];
+ for each(var xmlNode in xml.items.item) {
+ var item = Zotero.Sync.Server.Data.xmlToItem(xmlNode, false, true);
+ if (item.isRegularItem() || !item.getSource()) {
+ var id = item.save();
+ toAdd.push(id);
+ }
+ }
+ if (toAdd.length > 0) {
+ this._getItemAtRow(row).ref.addItems(toAdd);
+ }
+
+ Zotero.DB.commitTransaction();
+
+ return;
+ }
else if (dataType == 'text/x-moz-url' || dataType == 'application/x-moz-file') {
if (this._getItemAtRow(row).isCollection()) {
var parentCollectionID = this._getItemAtRow(row).ref.id;
@@ -820,6 +864,7 @@ Zotero.CollectionTreeView.prototype.getSupportedFlavours = function ()
var flavors = new FlavourSet();
flavors.appendFlavour("zotero/collection");
flavors.appendFlavour("zotero/item");
+ flavors.appendFlavour("zotero/item-xml");
flavors.appendFlavour("text/x-moz-url");
flavors.appendFlavour("application/x-moz-file", "nsIFile");
return flavors;
@@ -884,6 +929,11 @@ Zotero.ItemGroup.prototype.isSearch = function()
return this.type == 'search';
}
+Zotero.ItemGroup.prototype.isShare = function()
+{
+ return this.type == 'share';
+}
+
Zotero.ItemGroup.prototype.getName = function()
{
if (this.isCollection()) {
@@ -895,6 +945,9 @@ Zotero.ItemGroup.prototype.getName = function()
else if (this.isSearch()) {
return this.ref.name;
}
+ else if (this.isShare()) {
+ return this.ref.name;
+ }
else {
return "";
}
@@ -902,6 +955,11 @@ Zotero.ItemGroup.prototype.getName = function()
Zotero.ItemGroup.prototype.getChildItems = function()
{
+ // Fake results if this is a shared library
+ if (this.isShare()) {
+ return this.ref.getAll();
+ }
+
var s = this.getSearchObject();
try {
var ids = s.search();
@@ -970,6 +1028,11 @@ Zotero.ItemGroup.prototype.getSearchObject = function() {
* Returns all the tags used by items in the current view
*/
Zotero.ItemGroup.prototype.getChildTags = function() {
+ // TODO: implement?
+ if (this.isShare()) {
+ return false;
+ }
+
var s = this.getSearchObject();
return Zotero.Tags.getAllWithinSearch(s);
}
diff --git a/chrome/content/zotero/xpcom/data/item.js b/chrome/content/zotero/xpcom/data/item.js
index 3707682e5..15cc4f46c 100644
--- a/chrome/content/zotero/xpcom/data/item.js
+++ b/chrome/content/zotero/xpcom/data/item.js
@@ -1765,6 +1765,10 @@ Zotero.Item.prototype.getNoteTitle = function() {
return this._noteTitle;
}
+ if (!this.id) {
+ return '';
+ }
+
var sql = "SELECT title FROM itemNotes WHERE itemID=?";
var title = Zotero.DB.valueQuery(sql, this.id);
@@ -1782,10 +1786,6 @@ Zotero.Item.prototype.getNote = function() {
throw ("getNote() can only be called on notes and attachments");
}
- if (!this.id) {
- return '';
- }
-
// Store access time for later garbage collection
this._noteAccessTime = new Date();
@@ -1793,6 +1793,10 @@ Zotero.Item.prototype.getNote = function() {
return this._noteText;
}
+ if (!this.id) {
+ return '';
+ }
+
var sql = "SELECT note FROM itemNotes WHERE itemID=" + this.id;
var note = Zotero.DB.valueQuery(sql);
diff --git a/chrome/content/zotero/xpcom/data/tags.js b/chrome/content/zotero/xpcom/data/tags.js
index 82e809bfd..2e3d6b2a6 100644
--- a/chrome/content/zotero/xpcom/data/tags.js
+++ b/chrome/content/zotero/xpcom/data/tags.js
@@ -163,7 +163,8 @@ Zotero.Tags = new function() {
var tmpTable = search.search(true);
}
catch (e) {
- if (e.match(/Saved search [0-9]+ does not exist/)) {
+ if (typeof e == 'string'
+ && e.match(/Saved search [0-9]+ does not exist/)) {
Zotero.DB.rollbackTransaction();
Zotero.debug(e, 2);
}
diff --git a/chrome/content/zotero/xpcom/dataServer.js b/chrome/content/zotero/xpcom/dataServer.js
new file mode 100644
index 000000000..054abb412
--- /dev/null
+++ b/chrome/content/zotero/xpcom/dataServer.js
@@ -0,0 +1,271 @@
+Zotero.DataServer = new function () {
+ this.init = init;
+ this.handleHeader = handleHeader;
+
+ // TODO: assign dynamically
+ this.__defineGetter__('port', function () {
+ return 22030;
+ });
+
+ var _onlineObserverRegistered;
+
+
+ /*
+ * initializes a very rudimentary web server used for SOAP RPC
+ */
+ function init() {
+ // Use Zeroconf pref for now
+ if (!Zotero.Prefs.get("zeroconf.server.enabled")) {
+ Zotero.debug("Not initializing data HTTP server");
+ return;
+ }
+
+ if (Zotero.Utilities.HTTP.browserIsOffline()) {
+ Zotero.debug('Browser is offline -- not initializing data HTTP server');
+ _registerOnlineObserver()
+ return;
+ }
+
+ // start listening on socket
+ var serv = Components.classes["@mozilla.org/network/server-socket;1"]
+ .createInstance(Components.interfaces.nsIServerSocket);
+ try {
+ serv.init(this.port, false, -1);
+ serv.asyncListen(Zotero.DataServer.SocketListener);
+
+ Zotero.debug("Data HTTP server listening on 127.0.0.1:" + serv.port);
+ }
+ catch(e) {
+ Zotero.debug("Not initializing data HTTP server");
+ }
+
+ _registerOnlineObserver()
+ }
+
+ /*
+ * handles an HTTP request
+ */
+ function handleHeader(header) {
+ // get first line of request (all we care about for now)
+ var method = header.substr(0, header.indexOf(" "));
+
+ if (!method) {
+ return _generateResponse("400 Bad Request");
+ }
+
+ if (method != "POST") {
+ return _generateResponse("501 Method Not Implemented");
+ }
+
+ // Parse request URI
+ var matches = header.match("^[A-Z]+ (\/.*) HTTP/1.[01]");
+ if (!matches) {
+ return _generateResponse("400 Bad Request");
+ }
+
+ var response = _handleRequest(matches[1]);
+
+ // return OK
+ return _generateResponse("200 OK", 'text/xml; charset="UTF-8"', response);
+ }
+
+
+ function _handleRequest(uri) {
+ var s = new Zotero.Search();
+ s.addCondition('noChildren', 'true');
+ var ids = s.search();
+
+ if (!ids) {
+ ids = [];
+ }
+
+ var uploadIDs = {
+ updated: {
+ items: ids
+ },
+ /* TODO: fix buildUploadXML to ignore missing */
+ deleted: {}
+ };
+ return Zotero.Sync.Server.Data.buildUploadXML(uploadIDs);
+ }
+
+
+ /*
+ * generates the response to an HTTP request
+ */
+ function _generateResponse(status, contentType, body) {
+ var response = "HTTP/1.0 "+status+"\r\n";
+
+ if(body) {
+ if(contentType) {
+ response += "Content-Type: "+contentType+"\r\n";
+ }
+ response += "\r\n"+body;
+ } else {
+ response += "Content-Length: 0\r\n\r\n"
+ }
+
+ return response;
+ }
+
+
+ function _registerOnlineObserver() {
+ if (_onlineObserverRegistered) {
+ return;
+ }
+
+ // Observer to enable the integration when we go online
+ var observer = {
+ observe: function(subject, topic, data) {
+ if (data == 'online') {
+ Zotero.Integration.init();
+ }
+ }
+ };
+
+ var observerService =
+ Components.classes["@mozilla.org/observer-service;1"]
+ .getService(Components.interfaces.nsIObserverService);
+ observerService.addObserver(observer, "network:offline-status-changed", false);
+
+ _onlineObserverRegistered = true;
+ }
+}
+
+
+Zotero.DataServer.SocketListener = new function() {
+ this.onSocketAccepted = onSocketAccepted;
+
+ /*
+ * called when a socket is opened
+ */
+ function onSocketAccepted(socket, transport) {
+ // get an input stream
+ var iStream = transport.openInputStream(0, 0, 0);
+ var oStream = transport.openOutputStream(0, 0, 0);
+
+ var dataListener = new Zotero.DataServer.DataListener(iStream, oStream);
+ var pump = Components.classes["@mozilla.org/network/input-stream-pump;1"]
+ .createInstance(Components.interfaces.nsIInputStreamPump);
+ pump.init(iStream, -1, -1, 0, 0, false);
+ pump.asyncRead(dataListener, null);
+ }
+}
+
+/*
+ * handles the actual acquisition of data
+ */
+Zotero.DataServer.DataListener = function(iStream, oStream) {
+ this.header = "";
+ this.headerFinished = false;
+
+ this.body = "";
+ this.bodyLength = 0;
+
+ this.iStream = iStream;
+ this.oStream = oStream;
+ this.sStream = Components.classes["@mozilla.org/scriptableinputstream;1"]
+ .createInstance(Components.interfaces.nsIScriptableInputStream);
+ this.sStream.init(iStream);
+
+ this.foundReturn = false;
+}
+
+/*
+ * called when a request begins (although the request should have begun before
+ * the DataListener was generated)
+ */
+Zotero.DataServer.DataListener.prototype.onStartRequest = function(request, context) {}
+
+/*
+ * called when a request stops
+ */
+Zotero.DataServer.DataListener.prototype.onStopRequest = function(request, context, status) {
+ this.iStream.close();
+ this.oStream.close();
+}
+
+/*
+ * called when new data is available
+ */
+Zotero.DataServer.DataListener.prototype.onDataAvailable = function(request, context,
+ inputStream, offset, count) {
+ var readData = this.sStream.read(count);
+
+ // Read header
+ if (!this.headerFinished) {
+ // see if there's a magic double return
+ var lineBreakIndex = readData.indexOf("\r\n\r\n");
+ if (lineBreakIndex != -1) {
+ if (lineBreakIndex != 0) {
+ this.header += readData.substr(0, lineBreakIndex+4);
+ }
+
+ this._headerFinished();
+ return;
+ }
+
+ var lineBreakIndex = readData.indexOf("\n\n");
+ if (lineBreakIndex != -1) {
+ if (lineBreakIndex != 0) {
+ this.header += readData.substr(0, lineBreakIndex+2);
+ }
+
+ this._headerFinished();
+ return;
+ }
+
+ if (this.header && this.header[this.header.length-1] == "\n" &&
+ (readData[0] == "\n" || readData[0] == "\r")) {
+ if (readData.length > 1 && readData[1] == "\n") {
+ this.header += readData.substr(0, 2);
+ }
+ else {
+ this.header += readData[0];
+ }
+
+ this._headerFinished();
+ return;
+ }
+
+ this.header += readData;
+ }
+}
+
+/*
+ * processes an HTTP header and decides what to do
+ */
+Zotero.DataServer.DataListener.prototype._headerFinished = function() {
+ this.headerFinished = true;
+ var output = Zotero.DataServer.handleHeader(this.header);
+ this._requestFinished(output);
+}
+
+/*
+ * returns HTTP data from a request
+ */
+Zotero.DataServer.DataListener.prototype._requestFinished = function(response) {
+ // close input stream
+ this.iStream.close();
+
+ // open UTF-8 converter for output stream
+ var intlStream = Components.classes["@mozilla.org/intl/converter-output-stream;1"]
+ .createInstance(Components.interfaces.nsIConverterOutputStream);
+
+ // write
+ try {
+ intlStream.init(this.oStream, "UTF-8", 1024, "?".charCodeAt(0));
+
+ Zotero.debug('Writing response to stream:\n\n' + response);
+
+ // write response
+ intlStream.writeString(response);
+ } catch(e) {
+ Zotero.debug("An error occurred.");
+ Zotero.debug(e);
+ } finally {
+ Zotero.debug('Closing stream');
+ intlStream.close();
+ }
+}
+
diff --git a/chrome/content/zotero/xpcom/itemTreeView.js b/chrome/content/zotero/xpcom/itemTreeView.js
index 5b64b5f57..ff9ca4f6b 100644
--- a/chrome/content/zotero/xpcom/itemTreeView.js
+++ b/chrome/content/zotero/xpcom/itemTreeView.js
@@ -47,7 +47,7 @@ Zotero.ItemTreeView = function(itemGroup, sourcesOnly)
this._dataItems = [];
this.rowCount = 0;
- this._unregisterID = Zotero.Notifier.registerObserver(this, ['item', 'collection-item']);
+ this._unregisterID = Zotero.Notifier.registerObserver(this, ['item', 'collection-item', 'share-items']);
}
@@ -229,6 +229,13 @@ Zotero.ItemTreeView.prototype.refresh = function()
}
+Zotero.ItemTreeView.prototype.__defineGetter__('readOnly', function () {
+ if (this._itemGroup.isShare()) {
+ return true;
+ }
+ return false;
+});
+
/*
* Called by Zotero.Notifier on any changes to items in the data layer
*/
@@ -251,7 +258,12 @@ Zotero.ItemTreeView.prototype.notify = function(action, type, ids, extraData)
// If refreshing a single item, just unselect and reselect it
if (action == 'refresh') {
- if (savedSelection.length == 1 && savedSelection[0] == ids[0]) {
+ if (type == 'share-items') {
+ if (this._itemGroup.isShare()) {
+ this.refresh();
+ }
+ }
+ else if (savedSelection.length == 1 && savedSelection[0] == ids[0]) {
this.selection.clearSelection();
this.rememberSelection(savedSelection);
}
@@ -259,6 +271,10 @@ Zotero.ItemTreeView.prototype.notify = function(action, type, ids, extraData)
return;
}
+ if (this._itemGroup.isShare()) {
+ return;
+ }
+
this.selection.selectEventsSuppressed = true;
// See if we're in the active window
@@ -1502,7 +1518,22 @@ Zotero.ItemTreeCommandController.prototype.onEvent = function(evt)
* Begin a drag
*/
Zotero.ItemTreeView.prototype.onDragStart = function (evt,transferData,action)
-{
+{
+ // Quick implementation of dragging of XML item format
+ if (this.readOnly) {
+ var items = this.getSelectedItems();
+
+ var xml = ;
+ for (var i=0; i /tmp/zoteroconf_instances &
+
+elif [ $1 = "kill_find_instances" ]; then
+ PIDs=`ps x | grep "dns-sd -B" | grep _zotero._tcp | sed -E 's/ *([0-9]+).*/\1/' | xargs`
+ if [ "$PIDs" ]; then
+ kill $PIDs
+ fi
+
+elif [ $1 = "get_info" ]; then
+ if [ ! "$2" ]; then
+ echo "Service name not specified"
+ exit 1
+ fi
+
+ if [ ! "$3" ]; then
+ echo "Temp file path not specified"
+ exit 1
+ fi
+
+ #dns-sd -L "$2" _zotero._tcp local. > $3 &
+ mDNS -L "$2" _zotero._tcp local. > $3 &
+
+elif [ $1 = "kill_get_info" ]; then
+ #PIDs=`ps x | grep "dns-sd -L" | grep _zotero._tcp | sed -E 's/ *([0-9]+).*/\1/' | xargs`
+ PIDs=`ps x | grep "mDNS -L" | grep _zotero._tcp | sed -E 's/ *([0-9]+).*/\1/' | xargs`
+ if [ "$PIDs" ]; then
+ kill $PIDs
+ fi
+
+elif [ $1 = "kill_service" ]; then
+ PIDs=`ps x | grep dns-sd | grep '_zotero._tcp' | sed -E 's/ *([0-9]+).*/\1/' | xargs`
+ if [ "$PIDs" ]; then
+ kill $PIDs
+ fi
+fi