www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | Submodules | README | LICENSE

creators.js (7487B)


      1 /*
      2     ***** BEGIN LICENSE BLOCK *****
      3     
      4     Copyright © 2009 Center for History and New Media
      5                      George Mason University, Fairfax, Virginia, USA
      6                      http://zotero.org
      7     
      8     This file is part of Zotero.
      9     
     10     Zotero is free software: you can redistribute it and/or modify
     11     it under the terms of the GNU Affero General Public License as published by
     12     the Free Software Foundation, either version 3 of the License, or
     13     (at your option) any later version.
     14     
     15     Zotero is distributed in the hope that it will be useful,
     16     but WITHOUT ANY WARRANTY; without even the implied warranty of
     17     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     18     GNU Affero General Public License for more details.
     19     
     20     You should have received a copy of the GNU Affero General Public License
     21     along with Zotero.  If not, see <http://www.gnu.org/licenses/>.
     22     
     23     ***** END LICENSE BLOCK *****
     24 */
     25 
     26 
     27 Zotero.Creators = new function() {
     28 	this.fields = ['firstName', 'lastName', 'fieldMode'];
     29 	this.totes = 0;
     30 	
     31 	var _cache = {};
     32 	
     33 	this.init = Zotero.Promise.coroutine(function* () {
     34 		var repaired = false;
     35 		var sql = "SELECT * FROM creators";
     36 		var rows = yield Zotero.DB.queryAsync(sql);
     37 		for (let i = 0; i < rows.length; i++) {
     38 			let row = rows[i];
     39 			try {
     40 				_cache[row.creatorID] = this.cleanData({
     41 					// Avoid "DB column 'name' not found" warnings from the DB row Proxy
     42 					firstName: row.firstName,
     43 					lastName: row.lastName,
     44 					fieldMode: row.fieldMode
     45 				});
     46 			}
     47 			catch (e) {
     48 				// Automatically fix DB errors and try again
     49 				if (!repaired) {
     50 					Zotero.logError(e);
     51 					Zotero.logError("Trying integrity check to fix creator error");
     52 					yield Zotero.Schema.integrityCheck(true);
     53 					repaired = true;
     54 					rows = yield Zotero.DB.queryAsync(sql);
     55 					i = -1;
     56 					continue;
     57 				}
     58 				
     59 				throw e;
     60 			}
     61 		}
     62 	});
     63 	
     64 	/*
     65 	 * Returns creator data in internal format for a given creatorID
     66 	 */
     67 	this.get = function (creatorID) {
     68 		if (!creatorID) {
     69 			throw new Error("creatorID not provided");
     70 		}
     71 		
     72 		if (!_cache[creatorID]) {
     73 			throw new Error("Creator " + creatorID + " not found");
     74 		}
     75 		
     76 		// Return copy of data
     77 		return this.cleanData(_cache[creatorID]);
     78 	};
     79 	
     80 	
     81 	this.getItemsWithCreator = function (creatorID) {
     82 		var sql = "SELECT DISTINCT itemID FROM itemCreators WHERE creatorID=?";
     83 		return Zotero.DB.columnQueryAsync(sql, creatorID);
     84 	}
     85 	
     86 	
     87 	this.countItemAssociations = function (creatorID) {
     88 		var sql = "SELECT COUNT(*) FROM itemCreators WHERE creatorID=?";
     89 		return Zotero.DB.valueQueryAsync(sql, creatorID);
     90 	}
     91 	
     92 	
     93 	/**
     94 	 * Returns the creatorID matching given fields, or creates a new creator and returns its id
     95 	 *
     96 	 * @requireTransaction
     97 	 * @param {Object} data  Creator data in API JSON format
     98 	 * @param {Boolean} [create=false]  If no matching creator, create one
     99 	 * @return {Promise<Integer>}  creatorID
    100 	 */
    101 	this.getIDFromData = Zotero.Promise.coroutine(function* (data, create) {
    102 		Zotero.DB.requireTransaction();
    103 		data = this.cleanData(data);
    104 		var sql = "SELECT creatorID FROM creators WHERE "
    105 			+ "firstName=? AND lastName=? AND fieldMode=?";
    106 		var id = yield Zotero.DB.valueQueryAsync(
    107 			sql, [data.firstName, data.lastName, data.fieldMode]
    108 		);
    109 		if (!id && create) {
    110 			id = Zotero.ID.get('creators');
    111 			let sql = "INSERT INTO creators (creatorID, firstName, lastName, fieldMode) "
    112 				+ "VALUES (?, ?, ?, ?)";
    113 			yield Zotero.DB.queryAsync(
    114 				sql, [id, data.firstName, data.lastName, data.fieldMode]
    115 			);
    116 			_cache[id] = data;
    117 		}
    118 		return id;
    119 	});
    120 	
    121 	
    122 	this.updateCreator = Zotero.Promise.coroutine(function* (creatorID, creatorData) {
    123 		var creator = yield this.get(creatorID);
    124 		if (!creator) {
    125 			throw new Error("Creator " + creatorID + " doesn't exist");
    126 		}
    127 		creator.fieldMode = creatorData.fieldMode;
    128 		creator.firstName = creatorData.firstName;
    129 		creator.lastName = creatorData.lastName;
    130 		return creator.save();
    131 	});
    132 	
    133 	
    134 	/**
    135 	 * Delete obsolete creator rows from database and clear internal cache entries
    136 	 *
    137 	 * @return {Promise}
    138 	 */
    139 	this.purge = Zotero.Promise.coroutine(function* () {
    140 		if (!Zotero.Prefs.get('purge.creators')) {
    141 			return;
    142 		}
    143 		
    144 		Zotero.debug("Purging creator tables");
    145 		
    146 		var sql = 'SELECT creatorID FROM creators WHERE creatorID NOT IN '
    147 			+ '(SELECT creatorID FROM itemCreators)';
    148 		var toDelete = yield Zotero.DB.columnQueryAsync(sql);
    149 		if (toDelete.length) {
    150 			// Clear creator entries in internal array
    151 			for (let i=0; i<toDelete.length; i++) {
    152 				delete _cache[toDelete[i]];
    153 			}
    154 			
    155 			var sql = "DELETE FROM creators WHERE creatorID NOT IN "
    156 				+ "(SELECT creatorID FROM itemCreators)";
    157 			yield Zotero.DB.queryAsync(sql);
    158 		}
    159 		
    160 		Zotero.Prefs.set('purge.creators', false);
    161 	});
    162 	
    163 	
    164 	this.equals = function (data1, data2) {
    165 		data1 = this.cleanData(data1);
    166 		data2 = this.cleanData(data2);
    167 		return data1.lastName === data2.lastName
    168 			&& data1.firstName === data2.firstName
    169 			&& data1.fieldMode === data2.fieldMode
    170 			&& data1.creatorTypeID === data2.creatorTypeID;
    171 	},
    172 	
    173 	
    174 	this.cleanData = function (data) {
    175 		// Validate data
    176 		if (data.name === undefined && data.lastName === undefined) {
    177 			throw new Error("Creator data must contain either 'name' or 'firstName'/'lastName' properties");
    178 		}
    179 		if (data.name !== undefined && (data.firstName !== undefined || data.lastName !== undefined)) {
    180 			throw new Error("Creator data cannot contain both 'name' and 'firstName'/'lastName' properties");
    181 		}
    182 		if (data.name !== undefined && data.fieldMode === 0) {
    183 			throw new Error("'fieldMode' cannot be 0 with 'name' property");
    184 		}
    185 		if (data.fieldMode === 1
    186 				&& !(data.firstName === undefined || data.firstName === "" || data.firstName === null)) {
    187 			throw new Error("'fieldMode' cannot be 1 with 'firstName' property");
    188 		}
    189 		if (data.name !== undefined && typeof data.name != 'string') {
    190 			throw new Error("'name' must be a string");
    191 		}
    192 		if (data.firstName !== undefined && data.firstName !== null && typeof data.firstName != 'string') {
    193 			throw new Error("'firstName' must be a string");
    194 		}
    195 		if (data.lastName !== undefined && typeof data.lastName != 'string') {
    196 			throw new Error("'lastName' must be a string");
    197 		}
    198 		
    199 		var cleanedData = {
    200 			fieldMode: 0,
    201 			firstName: '',
    202 			lastName: ''
    203 		};
    204 		for (let i=0; i<this.fields.length; i++) {
    205 			let field = this.fields[i];
    206 			let val = data[field];
    207 			switch (field) {
    208 			case 'firstName':
    209 			case 'lastName':
    210 				if (val === undefined || val === null) continue;
    211 				cleanedData[field] = val.trim().normalize();
    212 				break;
    213 			
    214 			case 'fieldMode':
    215 				cleanedData[field] = val ? parseInt(val) : 0;
    216 				break;
    217 			}
    218 		}
    219 		
    220 		// Handle API JSON .name
    221 		if (data.name !== undefined) {
    222 			cleanedData.lastName = data.name.trim().normalize();
    223 			cleanedData.fieldMode = 1;
    224 		}
    225 		
    226 		var creatorType = data.creatorType || data.creatorTypeID;
    227 		if (creatorType) {
    228 			cleanedData.creatorTypeID = Zotero.CreatorTypes.getID(creatorType);
    229 			if (!cleanedData.creatorTypeID) {
    230 				let msg = "'" + creatorType + "' isn't a valid creator type";
    231 				Zotero.debug(msg, 2);
    232 				Components.utils.reportError(msg);
    233 			}
    234 		}
    235 		
    236 		return cleanedData;
    237 	}
    238 	
    239 	
    240 	this.internalToJSON = function (fields) {
    241 		var obj = {};
    242 		if (fields.fieldMode == 1) {
    243 			obj.name = fields.lastName;
    244 		}
    245 		else {
    246 			obj.firstName = fields.firstName;
    247 			obj.lastName = fields.lastName;
    248 		}
    249 		obj.creatorType = Zotero.CreatorTypes.getName(fields.creatorTypeID);
    250 		return obj;
    251 	}
    252 }