Addresses #502, Special handling for automatic tags
- Automatic tags now appear in orange; tooltip says either "User-added tag" or "Automatically added tag" - New menu in tag selector to toggle automatic tags - User and automatic tags are combined in tag selector, so renaming/deleting a tag will affect both user and automatic, regardless of view mode - Editing a tag makes it a user tag, as does adding an identical user tag to an item (rather than creating a second one) - ingester/export will need to be adjusted to add automatic tags Changed: Item.addTag(tag) => addTag(tag, type) Item.getTags() - now returns 'id', 'tag', 'type' Item.toArray() - tags now include 'type' property (from Item.getTags()) Tags.getID(tag) => getID(tag, type) Tags.getAll() => getAll([types]) - types is an optional array of tagTypes to fetch; now returns objects with 'tag' and 'type' properties Tags.getAllWithinSearch(search) => Tags.getAllWithinSearch(search, [types]) - now returns object with 'tag'/'type' Added: Tags.get(tagID) - returns object with 'tag' and 'type' properties Tags.getIDs(tag) - returns all tagIDs for this tag (of all types) Tags.getType(tag) - returns array of tag types matching given tag For type property, 0 == user, 1 == automatic
This commit is contained in:
parent
2f94234a36
commit
a3126da160
|
@ -50,7 +50,7 @@
|
|||
{
|
||||
for(var i = 0; i < tags.length; i++)
|
||||
{
|
||||
r = r + tags[i] + ", ";
|
||||
r = r + tags[i].tag + ", ";
|
||||
}
|
||||
r = r.substr(0,r.length-2);
|
||||
}
|
||||
|
@ -73,8 +73,7 @@
|
|||
|
||||
if(tags)
|
||||
{
|
||||
for(var i = 0; i < tags.length; i++)
|
||||
{
|
||||
for (var i=0; i<tags.length; i++) {
|
||||
this.addDynamicRow(tags[i], i+1);
|
||||
}
|
||||
this.updateCount(tags.length);
|
||||
|
@ -90,12 +89,18 @@
|
|||
</body>
|
||||
</method>
|
||||
<method name="addDynamicRow">
|
||||
<parameter name="tag"/>
|
||||
<parameter name="tagObj"/>
|
||||
<parameter name="tabindex"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
var id = tag ? Zotero.Tags.getID(tag) : null;
|
||||
tag = tag ? tag : '';
|
||||
if (tagObj) {
|
||||
var tagID = tagObj.id;
|
||||
var tag = tagObj.tag;
|
||||
var type = tagObj.type;
|
||||
}
|
||||
if (!tag) {
|
||||
tag = '';
|
||||
}
|
||||
|
||||
if (!tabindex)
|
||||
{
|
||||
|
@ -109,8 +114,17 @@
|
|||
}
|
||||
}
|
||||
|
||||
var icon= document.createElement("image");
|
||||
icon.setAttribute('src','chrome://zotero/skin/tag.png');
|
||||
var icon = document.createElement("image");
|
||||
var iconFile = 'tag';
|
||||
if (type == 0) {
|
||||
icon.setAttribute('tooltiptext', Zotero.getString('pane.item.tags.icon.user'));
|
||||
}
|
||||
else if (type == 1) {
|
||||
iconFile += '-automatic';
|
||||
icon.setAttribute('tooltiptext', Zotero.getString('pane.item.tags.icon.automatic'));
|
||||
}
|
||||
icon.setAttribute('src', 'chrome://zotero/skin/' + iconFile + '.png');
|
||||
|
||||
// DEBUG: Why won't just this.nextSibling.blur() work?
|
||||
icon.setAttribute('onclick','if (this.nextSibling.inputField){ this.nextSibling.inputField.blur() }');
|
||||
|
||||
|
@ -119,10 +133,10 @@
|
|||
var remove = document.createElement("label");
|
||||
remove.setAttribute('value','-');
|
||||
remove.setAttribute('class','zotero-clicky');
|
||||
if (id)
|
||||
if (tagID)
|
||||
{
|
||||
remove.setAttribute('ztabindex', -1);
|
||||
remove.setAttribute('onclick',"this.parentNode.parentNode.parentNode.parentNode.parentNode.remove('"+id+"');");
|
||||
remove.setAttribute('onclick',"this.parentNode.parentNode.parentNode.parentNode.parentNode.remove('"+ tagID +"');");
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -133,9 +147,11 @@
|
|||
row.appendChild(icon);
|
||||
row.appendChild(label);
|
||||
row.appendChild(remove);
|
||||
if (id)
|
||||
|
||||
if (tagID)
|
||||
{
|
||||
row.setAttribute('id','tag-'+id);
|
||||
row.setAttribute('id', 'tag-' + tagID);
|
||||
row.setAttribute('tagType', type);
|
||||
}
|
||||
|
||||
this.id('tagRows').appendChild(row);
|
||||
|
@ -246,6 +262,15 @@
|
|||
]]>
|
||||
</body>
|
||||
</method>
|
||||
<method name="closePopup">
|
||||
<body>
|
||||
<![CDATA[
|
||||
if (this.parentNode.getAttribute('showing')=='true') {
|
||||
this.parentNode.setAttribute('showing', false);
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
<method name="getScrollBox">
|
||||
<body>
|
||||
<![CDATA[
|
||||
|
|
|
@ -39,6 +39,18 @@
|
|||
<field name="_dirty">null</field>
|
||||
<field name="_empty">null</field>
|
||||
<field name="selection"/>
|
||||
<property name="showAutomatic" onget="return this.getAttribute('showAutomatic')"/>
|
||||
<property name="_types">
|
||||
<getter>
|
||||
<![CDATA[
|
||||
var types = [0];
|
||||
if (this.showAutomatic == 'true') {
|
||||
types.push(1);
|
||||
}
|
||||
return types;
|
||||
]]>
|
||||
</getter>
|
||||
</property>
|
||||
|
||||
<field name="_hasFilter">false</field>
|
||||
<field name="_filter">null</field>
|
||||
|
@ -99,6 +111,7 @@
|
|||
<constructor>
|
||||
<![CDATA[
|
||||
this.setAttribute('filterToScope', true);
|
||||
this.id('show-automatic').setAttribute('checked', this.getAttribute('showAutomatic'));
|
||||
this.dragObserver = new this._dragObserverConstructor;
|
||||
]]>
|
||||
</constructor>
|
||||
|
@ -156,7 +169,7 @@
|
|||
var tagsToggleBox = this.id('tags-toggle');
|
||||
|
||||
if (fetch || this._dirty) {
|
||||
this._tags = Zotero.Tags.getAll();
|
||||
this._tags = Zotero.Tags.getAll(this._types);
|
||||
|
||||
// Remove children
|
||||
while (tagsToggleBox.hasChildNodes()){
|
||||
|
@ -165,11 +178,21 @@
|
|||
|
||||
// Regenerate list
|
||||
for (var tagID in this._tags) {
|
||||
// If the last tag was the same, add this tagID and tagType to it
|
||||
if (tagsToggleBox.lastChild &&
|
||||
tagsToggleBox.lastChild.getAttribute('value') == this._tags[tagID].tag) {
|
||||
Zotero.debug(this._tags[tagID].tag);
|
||||
tagsToggleBox.lastChild.setAttribute('tagID', tagsToggleBox.lastChild.getAttribute('tagID') + '-' + tagID);
|
||||
tagsToggleBox.lastChild.setAttribute('tagType', tagsToggleBox.lastChild.getAttribute('tagType') + '-' + this._tags[tagID].type);
|
||||
continue;
|
||||
}
|
||||
|
||||
var label = document.createElement('label');
|
||||
label.setAttribute('onclick', "this.parentNode.parentNode.parentNode.handleTagClick(this)");
|
||||
label.className = 'zotero-clicky';
|
||||
label.setAttribute('value', this._tags[tagID]);
|
||||
label.setAttribute('value', this._tags[tagID].tag);
|
||||
label.setAttribute('tagID', tagID);
|
||||
label.setAttribute('tagType', this._tags[tagID].type);
|
||||
label.setAttribute('context', 'tag-menu');
|
||||
label.setAttribute('ondragover', 'nsDragAndDrop.dragOver(event, this.parentNode.parentNode.parentNode.dragObserver)');
|
||||
label.setAttribute('ondragexit', 'nsDragAndDrop.dragExit(event, this.parentNode.parentNode.parentNode.dragObserver)');
|
||||
|
@ -183,7 +206,7 @@
|
|||
// Set attributes
|
||||
var labels = tagsToggleBox.getElementsByTagName('label');
|
||||
for (var i=0; i<labels.length; i++){
|
||||
var tagID = labels[i].getAttribute('tagID');
|
||||
var tagIDs = labels[i].getAttribute('tagID').split('-');
|
||||
|
||||
// Restore selection
|
||||
if (this.selection[labels[i].value]){
|
||||
|
@ -193,13 +216,35 @@
|
|||
labels[i].setAttribute('selected', 'false');
|
||||
}
|
||||
|
||||
// Check tags against filter
|
||||
if (this._hasFilter) {
|
||||
var inFilter = false;
|
||||
for each(var tagID in tagIDs) {
|
||||
if (this._filter[tagID]) {
|
||||
inFilter = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check tags against scope
|
||||
if (this._hasScope) {
|
||||
var inScope = false;
|
||||
for each(var tagID in tagIDs) {
|
||||
if (this._scope[tagID]) {
|
||||
inScope = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If not in filter, hide
|
||||
if (this._hasFilter && !this._filter[tagID]) {
|
||||
if (this._hasFilter && !inFilter) {
|
||||
//Zotero.debug(1);
|
||||
labels[i].setAttribute('hidden', true);
|
||||
}
|
||||
else if (this.filterToScope) {
|
||||
if (this._hasScope && this.scope[tagID]) {
|
||||
if (this._hasScope && inScope) {
|
||||
//Zotero.debug(2);
|
||||
labels[i].setAttribute('inScope', true);
|
||||
labels[i].setAttribute('hidden', false);
|
||||
|
@ -213,7 +258,7 @@
|
|||
}
|
||||
// Display all
|
||||
else {
|
||||
if (this._hasScope && this.scope[tagID]) {
|
||||
if (this._hasScope && inScope) {
|
||||
//Zotero.debug(4);
|
||||
labels[i].setAttribute('inScope', true);
|
||||
}
|
||||
|
@ -294,7 +339,7 @@
|
|||
<![CDATA[
|
||||
// If a selected tag no longer exists, deselect it
|
||||
if (event == 'delete') {
|
||||
this._tags = Zotero.Tags.getAll();
|
||||
this._tags = Zotero.Tags.getAll(this._types);
|
||||
|
||||
for (var tag in this.selection) {
|
||||
for each(var tag2 in this._tags) {
|
||||
|
@ -430,10 +475,15 @@
|
|||
|
||||
|
||||
<method name="rename">
|
||||
<parameter name="tagID"/>
|
||||
<parameter name="tagIDs"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
var oldName = Zotero.Tags.getName(tagID);
|
||||
tagIDs = tagIDs.split('-');
|
||||
// Convert to ints
|
||||
for (var i=0; i<tagIDs.length; i++) {
|
||||
tagIDs[i] = parseInt(tagIDs[i]);
|
||||
}
|
||||
var oldName = Zotero.Tags.getName(tagIDs[0]);
|
||||
|
||||
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
||||
.getService(Components.interfaces.nsIPromptService);
|
||||
|
@ -444,9 +494,21 @@
|
|||
Zotero.getString('pane.tagSelector.rename.message'),
|
||||
newName, '', {});
|
||||
|
||||
if (result && newName.value)
|
||||
{
|
||||
Zotero.Tags.rename(tagID, newName.value);
|
||||
if (result && newName.value) {
|
||||
Zotero.DB.beginTransaction();
|
||||
|
||||
// Add other ids with same tag
|
||||
var ids = Zotero.Tags.getIDs(oldName);
|
||||
for each(var id in ids) {
|
||||
if (tagIDs.indexOf(id) == -1) {
|
||||
tagIDs.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
for each(var tagID in tagIDs) {
|
||||
Zotero.Tags.rename(tagID, newName.value);
|
||||
}
|
||||
Zotero.DB.commitTransaction();
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
|
@ -454,9 +516,12 @@
|
|||
|
||||
|
||||
<method name="delete">
|
||||
<parameter name="tagID"/>
|
||||
<parameter name="tagIDs"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
tagIDs = tagIDs.split('-');
|
||||
var oldName = Zotero.Tags.getName(tagIDs[0]);
|
||||
|
||||
var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
||||
.getService(Components.interfaces.nsIPromptService);
|
||||
|
||||
|
@ -465,7 +530,21 @@
|
|||
Zotero.getString('pane.tagSelector.delete.message'));
|
||||
|
||||
if (confirmed) {
|
||||
Zotero.Tags.remove(tagID);
|
||||
Zotero.DB.beginTransaction();
|
||||
|
||||
// Add other ids with same tag
|
||||
var ids = Zotero.Tags.getIDs(oldName);
|
||||
for each(var id in ids) {
|
||||
if (tagIDs.indexOf(id) == -1) {
|
||||
tagIDs.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
for each(var tagID in tagIDs) {
|
||||
Zotero.Tags.remove(tagID);
|
||||
}
|
||||
|
||||
Zotero.DB.commitTransaction()
|
||||
}
|
||||
]]>
|
||||
</body>
|
||||
|
@ -572,6 +651,13 @@
|
|||
<xul:toolbarbutton id="search-cancel"
|
||||
oncommand="this.parentNode.focus(); this.parentNode.parentNode.parentNode.parentNode.handleKeyPress(true)" hidden="true"/>
|
||||
</xul:textbox>
|
||||
<xul:toolbarbutton id="view-settings-menu" tooltiptext="&zotero.toolbar.actions.label;"
|
||||
image="chrome://zotero/skin/tag-selector-menu.png" type="menu">
|
||||
<xul:menupopup id="view-settings-popup">
|
||||
<xul:menuitem id="show-automatic" label="Show automatic" autocheck="true" type="checkbox"
|
||||
oncommand="var ts = this.parentNode.parentNode.parentNode.parentNode.parentNode; ts._dirty = true; ts.setAttribute('showAutomatic', this.getAttribute('checked') == 'true')"/>
|
||||
</xul:menupopup>
|
||||
</xul:toolbarbutton>
|
||||
</xul:hbox>
|
||||
|
||||
<xul:hbox>
|
||||
|
|
|
@ -1153,7 +1153,7 @@ var ZoteroItemPane = new function()
|
|||
// (which causes a delete of the row),
|
||||
// clear the tab direction so we don't advance
|
||||
// when the notifier kicks in
|
||||
var existing = Zotero.Tags.getID(value);
|
||||
var existing = Zotero.Tags.getID(value, 0);
|
||||
if (existing && id != existing)
|
||||
{
|
||||
_tabDirection = false;
|
||||
|
@ -1170,9 +1170,20 @@ var ZoteroItemPane = new function()
|
|||
return;
|
||||
}
|
||||
}
|
||||
// New tag
|
||||
else
|
||||
{
|
||||
// If this is an existing automatic tag, it's going to be
|
||||
// deleted and the number of rows will stay the same,
|
||||
// so we have to compensate
|
||||
var existingTypes = Zotero.Tags.getTypes(value);
|
||||
if (existingTypes && existingTypes.indexOf(1) != -1) {
|
||||
_lastTabIndex--;
|
||||
}
|
||||
|
||||
var id = tagsbox.add(value);
|
||||
|
||||
// DEBUG: why does this need to continue if added?
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1183,8 +1194,14 @@ var ZoteroItemPane = new function()
|
|||
else
|
||||
{
|
||||
// Just remove the row
|
||||
var row = rows.removeChild(row);
|
||||
//
|
||||
// If there's an open popup, this throws NODE CANNOT BE FOUND
|
||||
try {
|
||||
var row = rows.removeChild(row);
|
||||
}
|
||||
catch (e) {}
|
||||
tagsbox.fixPopup();
|
||||
|
||||
_tabDirection = false;
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -136,7 +136,8 @@
|
|||
</tree>
|
||||
</vbox>
|
||||
<splitter id="zotero-tags-splitter" onmouseup="ZoteroPane.updateTagSelectorSize()"/>
|
||||
<zoterotagselector id="zotero-tag-selector" persist="height,collapsed" oncommand="ZoteroPane.updateTagFilter()"/>
|
||||
<zoterotagselector id="zotero-tag-selector" persist="height,collapsed,showAutomaticsvn st"
|
||||
oncommand="ZoteroPane.updateTagFilter()"/>
|
||||
</vbox>
|
||||
|
||||
<splitter id="zotero-tree-splitter" resizebefore="closest" resizeafter="closest"/>
|
||||
|
|
|
@ -1491,7 +1491,7 @@ Zotero.Item.prototype.getAttachmentLinkMode = function(){
|
|||
throw ("getAttachmentLinkMode() can only be called on items of type 'attachment'");
|
||||
}
|
||||
|
||||
if (this._fileLinkMode!==null){
|
||||
if (this._fileLinkMode !==null && this._fileLinkMode !==false){
|
||||
return this._fileLinkMode;
|
||||
}
|
||||
|
||||
|
@ -1573,7 +1573,7 @@ Zotero.Item.prototype.getBestSnapshot = function(){
|
|||
//
|
||||
// save() is not required for tag functions
|
||||
//
|
||||
Zotero.Item.prototype.addTag = function(tag){
|
||||
Zotero.Item.prototype.addTag = function(tag, type){
|
||||
if (!this.getID()){
|
||||
throw ('Cannot add tag to unsaved item in Item.addTag()');
|
||||
}
|
||||
|
@ -1587,11 +1587,34 @@ Zotero.Item.prototype.addTag = function(tag){
|
|||
return false;
|
||||
}
|
||||
|
||||
Zotero.DB.beginTransaction();
|
||||
var tagID = Zotero.Tags.getID(tag);
|
||||
if (!tagID){
|
||||
var tagID = Zotero.Tags.add(tag);
|
||||
if (!type) {
|
||||
type = 0;
|
||||
}
|
||||
|
||||
if (type !=0 && type !=1) {
|
||||
throw ('Invalid tag type in Item.addTag()');
|
||||
}
|
||||
|
||||
Zotero.DB.beginTransaction();
|
||||
var tagID = Zotero.Tags.getID(tag, type);
|
||||
var existingTypes = Zotero.Tags.getTypes(tag);
|
||||
|
||||
if (existingTypes) {
|
||||
// If existing automatic and adding identical user, remove automatic
|
||||
if (type == 0 && existingTypes.indexOf(1) != -1) {
|
||||
this.removeTag(Zotero.Tags.getID(tag, 1));
|
||||
}
|
||||
// If existing user and adding automatic, skip
|
||||
else if (type == 1 && existingTypes.indexOf(0) != -1) {
|
||||
Zotero.debug('Identical user tag already exists -- skipping automatic tag add');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tagID) {
|
||||
var tagID = Zotero.Tags.add(tag, type);
|
||||
}
|
||||
|
||||
try {
|
||||
var result = this.addTagByID(tagID);
|
||||
Zotero.DB.commitTransaction();
|
||||
|
@ -1651,10 +1674,10 @@ Zotero.Item.prototype.hasTag = function(tagID) {
|
|||
}
|
||||
|
||||
Zotero.Item.prototype.getTags = function(){
|
||||
var sql = "SELECT tag FROM tags WHERE tagID IN "
|
||||
var sql = "SELECT tagID AS id, tag, tagType AS type FROM tags WHERE tagID IN "
|
||||
+ "(SELECT tagID FROM itemTags WHERE itemID=" + this.getID() + ") "
|
||||
+ "ORDER BY tag COLLATE NOCASE";
|
||||
return Zotero.DB.columnQuery(sql);
|
||||
return Zotero.DB.query(sql);
|
||||
}
|
||||
|
||||
Zotero.Item.prototype.getTagIDs = function(){
|
||||
|
@ -3141,11 +3164,14 @@ Zotero.Creators = new function(){
|
|||
* Same structure as Zotero.Creators -- make changes in both places if possible
|
||||
*/
|
||||
Zotero.Tags = new function(){
|
||||
var _tags = new Array; // indexed by tag text
|
||||
var _tagsByID = new Array; // indexed by tagID
|
||||
var _tags = []; // indexed by tag text
|
||||
var _tagsByID = []; // indexed by tagID
|
||||
|
||||
this.get = get;
|
||||
this.getName = getName;
|
||||
this.getID = getID;
|
||||
this.getIDs = getIDs;
|
||||
this.getTypes = getTypes;
|
||||
this.getAll = getAll;
|
||||
this.getAllWithinSearch = getAllWithinSearch;
|
||||
this.getTagItems = getTagItems;
|
||||
|
@ -3156,53 +3182,100 @@ Zotero.Tags = new function(){
|
|||
this.purge = purge;
|
||||
|
||||
/*
|
||||
* Returns a tag for a given tagID
|
||||
* Returns a tag and type for a given tagID
|
||||
*/
|
||||
function getName(tagID){
|
||||
function get(tagID) {
|
||||
if (_tagsByID[tagID]){
|
||||
return _tagsByID[tagID];
|
||||
}
|
||||
|
||||
var sql = 'SELECT tag FROM tags WHERE tagID=' + tagID;
|
||||
var result = Zotero.DB.valueQuery(sql);
|
||||
var sql = 'SELECT tag, tagType FROM tags WHERE tagID=?';
|
||||
var result = Zotero.DB.rowQuery(sql, tagID);
|
||||
|
||||
if (!result){
|
||||
return false;
|
||||
}
|
||||
|
||||
_tagsByID[tagID] = result;
|
||||
_tagsByID[tagID] = {
|
||||
tag: result.tag,
|
||||
type: result.tagType
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns the tagID matching given tag
|
||||
* Returns a tag for a given tagID
|
||||
*/
|
||||
function getID(tag){
|
||||
if (_tags[tag]){
|
||||
return _tags[tag];
|
||||
function getName(tagID) {
|
||||
if (_tagsByID[tagID]){
|
||||
return _tagsByID[tagID].tag;
|
||||
}
|
||||
|
||||
var sql = 'SELECT tagID FROM tags WHERE tag=?';
|
||||
var tagID = Zotero.DB.valueQuery(sql, [{string:tag}]);
|
||||
var tag = this.get(tagID);
|
||||
|
||||
if (tagID){
|
||||
_tags[tag] = tagID;
|
||||
return _tagsByID[tagID] ? _tagsByID[tagID].tag : false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns the tagID matching given tag and type
|
||||
*/
|
||||
function getID(tag, type) {
|
||||
if (_tags[type] && _tags[type][tag]){
|
||||
return _tags[type][tag];
|
||||
}
|
||||
|
||||
var sql = 'SELECT tagID FROM tags WHERE tag=? AND tagType=?';
|
||||
var tagID = Zotero.DB.valueQuery(sql, [tag, type]);
|
||||
|
||||
if (tagID) {
|
||||
if (!_tags[type]) {
|
||||
_tags[type] = [];
|
||||
}
|
||||
_tags[type][tag] = tagID;
|
||||
}
|
||||
|
||||
return tagID;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns all tagIDs for this tag (of all types)
|
||||
*/
|
||||
function getIDs(tag) {
|
||||
var sql = 'SELECT tagID FROM tags WHERE tag=?';
|
||||
return Zotero.DB.columnQuery(sql, [tag]);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Returns an array of tagTypes for tags matching given tag
|
||||
*/
|
||||
function getTypes(tag) {
|
||||
var sql = 'SELECT tagType FROM tags WHERE tag=?';
|
||||
return Zotero.DB.columnQuery(sql, [tag]);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get all tags indexed by tagID
|
||||
*
|
||||
* _types_ is an optional array of tagTypes to fetch
|
||||
*/
|
||||
function getAll(){
|
||||
var sql = 'SELECT tagID, tag FROM tags ORDER BY tag COLLATE NOCASE';
|
||||
function getAll(types) {
|
||||
var sql = "SELECT tagID, tag, tagType FROM tags ";
|
||||
if (types) {
|
||||
sql += "WHERE tagType IN (" + types.join() + ") ";
|
||||
}
|
||||
sql += "ORDER BY tag COLLATE NOCASE";
|
||||
var tags = Zotero.DB.query(sql);
|
||||
var indexed = {};
|
||||
for (var i in tags){
|
||||
indexed[tags[i]['tagID']] = tags[i]['tag'];
|
||||
for each(var tag in tags) {
|
||||
indexed[tag.tagID] = {
|
||||
tag: tag.tag,
|
||||
type: tag.tagType
|
||||
};
|
||||
}
|
||||
return indexed;
|
||||
}
|
||||
|
@ -3210,8 +3283,10 @@ Zotero.Tags = new function(){
|
|||
|
||||
/*
|
||||
* Get all tags within the items of a Zotero.Search object
|
||||
*
|
||||
* _types_ is an optional array of tagTypes to fetch
|
||||
*/
|
||||
function getAllWithinSearch(search) {
|
||||
function getAllWithinSearch(search, types) {
|
||||
// If search has post-search filters (e.g. fulltext content), run it
|
||||
// and just include the ids
|
||||
//
|
||||
|
@ -3220,19 +3295,30 @@ Zotero.Tags = new function(){
|
|||
// -- should probably use a temporary table instead
|
||||
if (search.hasPostSearchFilter()) {
|
||||
var ids = search.search();
|
||||
var sql = "SELECT DISTINCT tagID, tag FROM itemTags NATURAL JOIN tags "
|
||||
+ "WHERE itemID IN (" + ids.join() + ") ORDER BY tag COLLATE NOCASE";
|
||||
var sql = "SELECT DISTINCT tagID, tag, tagType FROM itemTags "
|
||||
+ "NATURAL JOIN tags WHERE itemID IN (" + ids.join() + ") ";
|
||||
if (types) {
|
||||
sql += "AND tagType IN (" + types.join() + ") ";
|
||||
}
|
||||
sql += "ORDER BY tag COLLATE NOCASE";
|
||||
var tags = Zotero.DB.query(sql);
|
||||
}
|
||||
else {
|
||||
var sql = "SELECT DISTINCT tagID, tag FROM itemTags NATURAL JOIN tags "
|
||||
+ "WHERE itemID IN (" + search.getSQL() + ") ORDER BY tag COLLATE NOCASE";
|
||||
var sql = "SELECT DISTINCT tagID, tag, tagType FROM itemTags "
|
||||
+ "NATURAL JOIN tags WHERE itemID IN (" + search.getSQL() + ") ";
|
||||
if (types) {
|
||||
sql += "AND tagType IN (" + types.join() + ") ";
|
||||
}
|
||||
sql += "ORDER BY tag COLLATE NOCASE";
|
||||
var tags = Zotero.DB.query(sql, search.getSQLParams());
|
||||
}
|
||||
|
||||
var indexed = {};
|
||||
for (var i in tags){
|
||||
indexed[tags[i]['tagID']] = tags[i]['tag'];
|
||||
for each(var tag in tags) {
|
||||
indexed[tag.tagID] = {
|
||||
tag: tag.tag,
|
||||
type: tag.tagType
|
||||
};
|
||||
}
|
||||
return indexed;
|
||||
}
|
||||
|
@ -3245,12 +3331,15 @@ Zotero.Tags = new function(){
|
|||
|
||||
|
||||
function search(str){
|
||||
var sql = 'SELECT tagID, tag FROM tags WHERE tag LIKE ? '
|
||||
var sql = 'SELECT tagID, tag, tagType FROM tags WHERE tag LIKE ? '
|
||||
+ 'ORDER BY tag COLLATE NOCASE';
|
||||
var tags = Zotero.DB.query(sql, '%' + str + '%');
|
||||
var indexed = {};
|
||||
for (var i in tags){
|
||||
indexed[tags[i]['tagID']] = tags[i]['tag'];
|
||||
for each(var tag in tags) {
|
||||
indexed[tag.tagID] = {
|
||||
tag: tag.tag,
|
||||
type: tag.tagType
|
||||
};
|
||||
}
|
||||
return indexed;
|
||||
}
|
||||
|
@ -3261,14 +3350,22 @@ Zotero.Tags = new function(){
|
|||
*
|
||||
* Returns new tagID
|
||||
*/
|
||||
function add(tag){
|
||||
Zotero.debug('Adding new tag', 4);
|
||||
function add(tag, type){
|
||||
if (type != 0 && type != 1) {
|
||||
throw ('Invalid tag type ' + type + ' in Tags.add()');
|
||||
}
|
||||
|
||||
if (!type) {
|
||||
type = 0;
|
||||
}
|
||||
|
||||
Zotero.debug('Adding new tag of type ' + type, 4);
|
||||
|
||||
Zotero.DB.beginTransaction();
|
||||
|
||||
var sql = 'INSERT INTO tags VALUES (?,?)';
|
||||
var sql = 'INSERT INTO tags VALUES (?,?,?)';
|
||||
var rnd = Zotero.getRandomID('tags', 'tagID');
|
||||
Zotero.DB.query(sql, [{int: rnd}, {string: tag}]);
|
||||
Zotero.DB.query(sql, [{int: rnd}, {string: tag}, {int: type}]);
|
||||
|
||||
Zotero.DB.commitTransaction();
|
||||
Zotero.Notifier.trigger('add', 'tag', rnd);
|
||||
|
@ -3281,8 +3378,21 @@ Zotero.Tags = new function(){
|
|||
|
||||
Zotero.DB.beginTransaction();
|
||||
|
||||
var tagObj = this.get(tagID);
|
||||
var oldName = tagObj.tag;
|
||||
var oldType = tagObj.type;
|
||||
|
||||
if (oldName == tag) {
|
||||
if (oldType != 0) {
|
||||
var sql = "UPDATE tags SET tagType=0 WHERE tagID=?";
|
||||
Zotero.DB.query(sql, tagID);
|
||||
}
|
||||
Zotero.DB.commitTransaction();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the new tag already exists
|
||||
var sql = "SELECT tagID FROM tags WHERE tag=?";
|
||||
var sql = "SELECT tagID FROM tags WHERE tag=? AND tagType=0";
|
||||
var existingTagID = Zotero.DB.valueQuery(sql, tag);
|
||||
if (existingTagID) {
|
||||
var itemIDs = this.getTagItems(tagID);
|
||||
|
@ -3296,7 +3406,9 @@ Zotero.Tags = new function(){
|
|||
// Manual purge of old tag
|
||||
var sql = "DELETE FROM tags WHERE tagID=?";
|
||||
Zotero.DB.query(sql, tagID);
|
||||
delete _tags[_tagsByID[tagID]];
|
||||
if (_tags[oldType]) {
|
||||
delete _tags[oldType][oldName];
|
||||
}
|
||||
delete _tagsByID[tagID];
|
||||
Zotero.Notifier.trigger('delete', 'tag', tagID);
|
||||
|
||||
|
@ -3321,12 +3433,15 @@ Zotero.Tags = new function(){
|
|||
return;
|
||||
}
|
||||
|
||||
var sql = "UPDATE tags SET tag=? WHERE tagID=?";
|
||||
// 0 == user tag -- we set all renamed tags to 0
|
||||
var sql = "UPDATE tags SET tag=?, tagType=0 WHERE tagID=?";
|
||||
Zotero.DB.query(sql, [{string: tag}, {int: tagID}]);
|
||||
|
||||
var itemIDs = this.getTagItems(tagID);
|
||||
|
||||
delete _tags[_tagsByID[tagID]];
|
||||
if (_tags[oldType]) {
|
||||
delete _tags[oldType][oldName];
|
||||
}
|
||||
delete _tagsByID[tagID];
|
||||
|
||||
Zotero.DB.commitTransaction();
|
||||
|
@ -3343,6 +3458,7 @@ Zotero.Tags = new function(){
|
|||
var itemIDs = Zotero.DB.columnQuery(sql, tagID);
|
||||
|
||||
if (!itemIDs) {
|
||||
Zotero.DB.commitTransaction();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -3368,26 +3484,35 @@ Zotero.Tags = new function(){
|
|||
* Returns removed tagIDs on success
|
||||
*/
|
||||
function purge(){
|
||||
var sql = 'SELECT tagID FROM tags WHERE tagID NOT IN '
|
||||
+ '(SELECT tagID FROM itemTags);';
|
||||
var toDelete = Zotero.DB.columnQuery(sql);
|
||||
Zotero.DB.beginTransaction();
|
||||
|
||||
var sql = 'SELECT tagID, tag, tagType FROM tags WHERE tagID '
|
||||
+ 'NOT IN (SELECT tagID FROM itemTags);';
|
||||
var toDelete = Zotero.DB.query(sql);
|
||||
|
||||
if (!toDelete){
|
||||
Zotero.DB.commitTransaction();
|
||||
return false;
|
||||
}
|
||||
|
||||
var purged = [];
|
||||
|
||||
// Clear tag entries in internal array
|
||||
for (var i=0; i<toDelete.length; i++){
|
||||
var tag = this.getName(toDelete[i]);
|
||||
delete _tags[tag];
|
||||
delete _tagsByID[toDelete[i]];
|
||||
for each(var tag in toDelete) {
|
||||
purged.push(tag.tagID);
|
||||
if (_tags[tag.tagType]) {
|
||||
delete _tags[tag.tagType][tag.tag];
|
||||
}
|
||||
delete _tagsByID[tag.tagID];
|
||||
}
|
||||
|
||||
sql = 'DELETE FROM tags WHERE tagID NOT IN '
|
||||
+ '(SELECT tagID FROM itemTags);';
|
||||
var result = Zotero.DB.query(sql);
|
||||
|
||||
Zotero.Notifier.trigger('delete', 'tag', toDelete);
|
||||
Zotero.DB.commitTransaction();
|
||||
|
||||
Zotero.Notifier.trigger('delete', 'tag', purged);
|
||||
|
||||
return toDelete;
|
||||
}
|
||||
|
|
|
@ -701,6 +701,15 @@ Zotero.Schema = new function(){
|
|||
if (i==15) {
|
||||
Zotero.DB.query("DROP TABLE IF EXISTS annotations");
|
||||
}
|
||||
|
||||
if (i==16) {
|
||||
Zotero.DB.query("CREATE TABLE tagsTemp (tagID INT, tag TEXT, PRIMARY KEY (tagID))");
|
||||
Zotero.DB.query("INSERT INTO tagsTemp SELECT * FROM tags");
|
||||
Zotero.DB.query("DROP TABLE tags");
|
||||
Zotero.DB.query("CREATE TABLE tags (\n tagID INT,\n tag TEXT,\n tagType INT,\n PRIMARY KEY (tagID),\n UNIQUE (tag, tagType)\n);");
|
||||
Zotero.DB.query("INSERT INTO tags SELECT tagID, tag, 0 FROM tagsTemp");
|
||||
Zotero.DB.query("DROP TABLE tagsTemp");
|
||||
}
|
||||
}
|
||||
|
||||
_updateSchema('userdata');
|
||||
|
|
BIN
chrome/skin/default/zotero/tag-automatic.png
Executable file
BIN
chrome/skin/default/zotero/tag-automatic.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 586 B |
BIN
chrome/skin/default/zotero/tag-selector-menu.png
Executable file
BIN
chrome/skin/default/zotero/tag-selector-menu.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 209 B |
|
@ -1,4 +1,4 @@
|
|||
-- 15
|
||||
-- 16
|
||||
|
||||
-- This file creates tables containing user-specific data -- any changes
|
||||
-- to existing tables made here must be mirrored in transition steps in
|
||||
|
@ -106,8 +106,10 @@ CREATE INDEX IF NOT EXISTS itemAttachments_mimeType ON itemAttachments(mimeType)
|
|||
-- Individual entries for each tag
|
||||
CREATE TABLE IF NOT EXISTS tags (
|
||||
tagID INT,
|
||||
tag TEXT UNIQUE,
|
||||
PRIMARY KEY (tagID)
|
||||
tag TEXT,
|
||||
tagType INT,
|
||||
PRIMARY KEY (tagID),
|
||||
UNIQUE (tag, tagType)
|
||||
);
|
||||
|
||||
-- Associates items with keywords
|
||||
|
|
Loading…
Reference in New Issue
Block a user