Addresses #17, add filesystem/ability to store files

Not finished, but enough to give David something to work with

No BLOBs -- just linking/importing of files and loaded documents


New Scholar.Item methods:

incrementFileCount() (used internally)
decrementFileCount() (used internally)
isFile()
numFiles()
getFile() -- returns nsILocalFile or false if associated file doesn't exist (note: always returns false for items with LINK_MODE_LINKED_URL, since they have no files -- use getFileURL() instead)
getFileURL() -- returns URL string
getFileLinkMode() -- compare to Scholar.Files.LINK_MODE_* constants: LINKED_FILE, IMPORTED_FILE, LINKED_URL, IMPORTED_URL
getFileMimeType() -- mime type of file (e.g. text/plain)
getFileCharset() -- charsetID of file
getFiles() -- array of file itemIDs this file is a source for

New Scholar.Files methods:

importFromFile(nsIFile file [, int sourceItemID])
linkFromFile(nsIFile file [, int sourceItemID])
importFromDocument(nsIDOMDocument document [, int sourceItemID])
linkFromDocument(nsIDOMDocument document [, int sourceItemID])

New class Scholar.FileTypes -- partially implemented, not yet used

New class  Scholar.CharacterSets -- same as other *Types classes:
getID(idOrName)
getName(idOrName)
getTypes() (aliased to getAll(), which I'll probably change the others to as well)

Charsets table with all official character sets (copied from Mozilla source)

Renamed Item.setNoteSource() to setSource() and Item.getNoteSource() to getSource() and adjusted to handle both notes and files
This commit is contained in:
Dan Stillman 2006-07-27 09:16:02 +00:00
parent 4959535aff
commit c50dedc90a
4 changed files with 929 additions and 41 deletions

View file

@ -44,6 +44,7 @@ Scholar.Item.prototype.isPrimaryField = function(field){
Scholar.Item.primaryFields = Scholar.DB.getColumnHash('items');
Scholar.Item.primaryFields['firstCreator'] = true;
Scholar.Item.primaryFields['numNotes'] = true;
Scholar.Item.primaryFields['numFiles'] = true;
}
return !!Scholar.Item.primaryFields[field];
@ -70,7 +71,8 @@ Scholar.Item.prototype.loadFromID = function(id){
var sql = 'SELECT I.*, lastName || '
+ 'CASE ((SELECT COUNT(*) FROM itemCreators WHERE itemID=' + id + ')>1) '
+ "WHEN 0 THEN '' ELSE ' et al.' END AS firstCreator, "
+ "(SELECT COUNT(*) FROM itemNotes WHERE sourceItemID=I.itemID) AS numNotes "
+ "(SELECT COUNT(*) FROM itemNotes WHERE sourceItemID=I.itemID) AS numNotes, "
+ "(SELECT COUNT(*) FROM itemFiles WHERE sourceItemID=I.itemID) AS numFiles "
+ 'FROM items I '
+ 'LEFT JOIN itemCreators IC ON (I.itemID=IC.itemID) '
+ 'LEFT JOIN creators C ON (IC.creatorID=C.creatorID) '
@ -774,11 +776,13 @@ Scholar.Item.prototype.updateDateModified = function(){
}
////////////////////////////////////////////////////////
//
// Methods dealing with item notes
// Methods dealing with note items
//
// save() is not required for note functions
//
////////////////////////////////////////////////////////
Scholar.Item.prototype.incrementNoteCount = function(){
this._data['numNotes']++;
}
@ -827,13 +831,21 @@ Scholar.Item.prototype.updateNote = function(text){
}
Scholar.Item.prototype.setNoteSource = function(sourceItemID){
if (!this.isNote()){
throw ("updateNote() can only be called on items of type 'note'");
Scholar.Item.prototype.setSource = function(sourceItemID){
if (this.isNote()){
var type = 'note';
var Type = 'Note';
}
else if (this.isFile()){
var type = 'file';
var Type = 'file';
}
else {
throw ("setSource() can only be called on items of type 'note' or 'file'");
}
if (!this.getID()){
throw ("Cannot call setNoteSource() on unsaved note");
throw ("Cannot call setSource() on unsaved " + type);
}
Scholar.DB.beginTransaction();
@ -842,15 +854,15 @@ Scholar.Item.prototype.setNoteSource = function(sourceItemID){
// FK check
if (sourceItemID && !newItem){
Scholar.DB.rollbackTransaction();
throw ("Cannot set note source to invalid item " + sourceItemID);
throw ("Cannot set " + type + " source to invalid item " + sourceItemID);
}
// Get previous source item id
var sql = "SELECT sourceItemID FROM itemNotes WHERE item=" + this.getID();
var sql = "SELECT sourceItemID FROM item" + Type + "s WHERE item=" + this.getID();
var oldSourceItemID = Scholar.DB.valueQuery(sql);
if (oldSourceItemID==sourceItemID){
Scholar.debug("Note source hasn't changed", 4);
Scholar.debug(Type + " source hasn't changed", 4);
Scholar.DB.commitTransaction();
return false;
}
@ -858,10 +870,10 @@ Scholar.Item.prototype.setNoteSource = function(sourceItemID){
var oldItem = Scholar.Items.get(oldSourceItemID);
if (oldSourceItemID && !oldItem){
Scholar.debug("Old source item " + oldSourceItemID
+ "didn't exist in setNoteSource()", 2);
+ "didn't exist in setSource()", 2);
}
var sql = "UPDATE itemNotes SET sourceItemID=? WHERE itemID=?";
var sql = "UPDATE item" + Type + "s SET sourceItemID=? WHERE itemID=?";
var bindParams = [sourceItemID ? {int:sourceItemID} : null, this.getID()];
Scholar.DB.query(sql, bindParams);
this.updateDateModified();
@ -869,13 +881,27 @@ Scholar.Item.prototype.setNoteSource = function(sourceItemID){
Scholar.Notifier.trigger('modify', 'item', this.getID());
// Update the note counts of the previous and new sources
// Update the counts of the previous and new sources
if (oldItem){
oldItem.decrementNoteCount();
switch (type){
case 'note':
oldItem.decrementNoteCount();
break;
case 'file':
oldItem.decrementFileCount();
break;
}
Scholar.Notifier.trigger('modify', 'item', oldSourceItemID);
}
if (newItem){
newItem.incrementNoteCount();
switch (type){
case 'note':
newItem.incrementNoteCount();
break;
case 'file':
newItem.incrementFileCount();
break;
}
Scholar.Notifier.trigger('modify', 'item', sourceItemID);
}
@ -914,14 +940,20 @@ Scholar.Item.prototype.getNote = function(){
/**
* Get the itemID of the source item for a note
* Get the itemID of the source item for a note or file
**/
Scholar.Item.prototype.getNoteSource = function(){
if (!this.isNote()){
throw ("getNoteSource() can only be called on items of type 'note'");
Scholar.Item.prototype.getSource = function(){
if (this.isNote()){
var Type = 'Note';
}
else if (this.isFile()){
var Type = 'File';
}
else {
throw ("getSource() can only be called on items of type 'note' or 'file'");
}
var sql = "SELECT sourceItemID FROM itemNotes WHERE itemID=" + this.getID();
var sql = "SELECT sourceItemID FROM item" + Type + "s WHERE itemID=" + this.getID();
return Scholar.DB.valueQuery(sql);
}
@ -945,6 +977,171 @@ Scholar.Item.prototype.getNotes = function(){
}
////////////////////////////////////////////////////////
//
// Methods dealing with file items
//
// save() is not required for file functions
//
///////////////////////////////////////////////////////
Scholar.Item.prototype.incrementFileCount = function(){
this._data['numFiles']++;
}
Scholar.Item.prototype.decrementFileCount = function(){
this._data['numFiles']--;
}
/**
* Determine if an item is a file
**/
Scholar.Item.prototype.isFile = function(){
return Scholar.ItemTypes.getName(this.getType())=='file';
}
/**
* Returns number of files in item
**/
Scholar.Item.prototype.numFiles = function(){
if (this.isFile()){
throw ("numFiles() cannot be called on items of type 'file'");
}
if (!this.getID()){
return 0;
}
return this._data['numFiles'];
}
/**
* Get an nsILocalFile for the file item, or false if the associated file doesn't exist
*
* Note: Always returns false for items with LINK_MODE_LINKED_URL,
* since they have no files -- use getFileURL() instead
**/
Scholar.Item.prototype.getFile = function(){
if (!this.isFile()){
throw ("getFile() can only be called on items of type 'file'");
}
var sql = "SELECT linkMode, path FROM itemFiles WHERE itemID=" + this.getID();
var row = Scholar.DB.rowQuery(sql);
if (!row){
throw ('File data not found for item ' + this.getID() + ' in getFile()');
}
// No associated files for linked URLs
if (row['linkMode']==Scholar.Files.LINK_MODE_LINKED_URL){
return false;
}
var file = Components.classes["@mozilla.org/file/local;1"].
createInstance(Components.interfaces.nsILocalFile);
var refDir = (linkMode==this.LINK_MODE_LINKED_FILE)
? Scholar.getScholarDirectory() : Scholar.getStorageDirectory();
file.setRelativeDescriptor(refDir, row['path']);
if (!file.exists()){
return false;
}
return file;
}
Scholar.Item.prototype.getFileURL = function(){
if (!this.isFile()){
throw ("getFileURL() can only be called on items of type 'file'");
}
var sql = "SELECT linkMode, path, originalPath FROM itemFiles "
+ "WHERE itemID=" + this.getID();
var row = Scholar.DB.rowQuery(sql);
if (!row){
throw ('File data not found for item ' + this.getID() + ' in getFileURL()');
}
switch (row['linkMode']){
case Scholar.Files.LINK_MODE_LINKED_URL:
return row['path'];
case Scholar.Files.LINK_MODE_IMPORTED_URL:
return row['originalPath'];
default:
throw ('getFileURL() cannot be called on files without associated URLs');
}
}
/**
* Get the link mode of a file item
*
* Possible return values specified as constants in Scholar.Files
* (e.g. Scholar.Files.LINK_MODE_LINKED_FILE)
**/
Scholar.Item.prototype.getFileLinkMode = function(){
if (!this.isFile()){
throw ("getFileLinkMode() can only be called on items of type 'file'");
}
var sql = "SELECT linkMode FROM itemFiles WHERE itemID=" + this.getID();
return Scholar.DB.valueQuery(sql);
}
/**
* Get the mime type of a file item (e.g. text/plain)
**/
Scholar.Item.prototype.getFileMimeType = function(){
if (!this.isFile()){
throw ("getFileData() can only be called on items of type 'file'");
}
var sql = "SELECT mimeType FROM itemFiles WHERE itemID=" + this.getID();
return Scholar.DB.valueQuery(sql);
}
/**
* Get the character set id of a file item
**/
Scholar.Item.prototype.getFileCharset = function(){
if (!this.isFile()){
throw ("getFileCharset() can only be called on items of type 'file'");
}
var sql = "SELECT charsetID FROM itemFiles WHERE itemID=" + this.getID();
return Scholar.DB.valueQuery(sql);
}
/**
* Returns an array of file itemIDs for this item
**/
Scholar.Item.prototype.getFiles = function(){
if (this.isFile()){
throw ("getFiles() cannot be called on items of type 'file'");
}
if (!this.getID()){
return [];
}
var sql = "SELECT itemID FROM itemFiles NATURAL JOIN items "
+ "WHERE sourceItemID=" + this.getID() + " ORDER BY dateAdded";
return Scholar.DB.columnQuery(sql);
}
//
// Methods dealing with item tags
//
@ -1069,8 +1266,9 @@ Scholar.Item.prototype.erase = function(){
Scholar.Collections.get(parentCollectionIDs[i]).removeItem(this.getID());
}
// If note, remove item from source notes
// Note
if (this.isNote()){
// Decrement note count of source items
var sql = "SELECT sourceItemID FROM itemNotes WHERE itemID=" + this.getID();
var sourceItemID = Scholar.DB.valueQuery(sql);
if (sourceItemID){
@ -1079,19 +1277,55 @@ Scholar.Item.prototype.erase = function(){
changedItems.push(sourceItemID);
}
}
// If not note, unassociate any notes for which this is a source
// File
else if (this.isFile()){
// Decrement file count of source items
var sql = "SELECT sourceItemID FROM itemFiles WHERE itemID=" + this.getID();
var sourceItemID = Scholar.DB.valueQuery(sql);
if (sourceItemID){
var sourceItem = Scholar.Items.get(sourceItemID);
sourceItem.decrementFileCount();
changedItems.push(sourceItemID);
}
// Delete associated files
var linkMode = this.getFileLinkMode();
switch (linkMode){
case Scholar.Files.LINK_MODE_LINKED_FILE:
case Scholar.Files.LINK_MODE_LINKED_URL:
// Links only -- nothing to delete
break;
default:
var file = Scholar.getStorageDirectory();
file.append(this.getID());
if (file.exists()){
file.remove(true);
}
}
}
// If regular item, unassociate any notes or files for which this is a source
else {
// TODO: option for deleting child notes instead of unlinking
// Notes
var sql = "SELECT itemID FROM itemNotes WHERE sourceItemID=" + this.getID();
var childNotes = Scholar.DB.columnQuery(sql);
if (childNotes){
changedItems.push(childNotes);
}
var sql = "UPDATE itemNotes SET sourceItemID=NULL WHERE sourceItemID="
+ this.getID();
Scholar.DB.query(sql);
// Files
var sql = "SELECT itemID FROM itemFiles WHERE sourceItemID=" + this.getID();
var childFiles = Scholar.DB.columnQuery(sql);
if (childFiles){
changedItems.push(childFiles);
}
var sql = "UPDATE itemFiles SET sourceItemID=NULL WHERE sourceItemID="
+ this.getID();
Scholar.DB.query(sql);
}
// Flag See Also links for notification
@ -1102,6 +1336,7 @@ Scholar.Item.prototype.erase = function(){
sql = 'DELETE FROM itemCreators WHERE itemID=' + this.getID() + ";\n";
sql += 'DELETE FROM itemNotes WHERE itemID=' + this.getID() + ";\n";
sql += 'DELETE FROM itemFiles WHERE itemID=' + this.getID() + ";\n";
sql += 'DELETE FROM itemSeeAlso WHERE itemID=' + this.getID() + ";\n";
sql += 'DELETE FROM itemSeeAlso WHERE linkedItemID=' + this.getID() + ";\n";
sql += 'DELETE FROM itemTags WHERE itemID=' + this.getID() + ";\n";
@ -1115,8 +1350,14 @@ Scholar.Item.prototype.erase = function(){
Scholar.DB.commitTransaction();
}
catch (e){
// On failure, reset count of source items
if (sourceItem){
sourceItem.incrementNoteCount();
if (this.isNote()){
sourceItem.incrementNoteCount();
}
else if (this.isFile()){
sourceItem.incrementFileCount();
}
}
Scholar.DB.rollbackTransaction();
throw (e);
@ -1167,6 +1408,7 @@ Scholar.Item.prototype.toArray = function(){
// Skip certain fields
case 'firstCreator':
case 'numNotes':
case 'numFiles':
continue;
// For the rest, just copy over
@ -1180,7 +1422,7 @@ Scholar.Item.prototype.toArray = function(){
arr[Scholar.ItemFields.getName(i)] = this._itemData[i];
}
if (!this.isNote()){
if (!this.isNote() && !this.isFile()){
// Creators
arr['creators'] = [];
var creators = this.getCreators();
@ -1192,9 +1434,20 @@ Scholar.Item.prototype.toArray = function(){
arr['creators'][i]['creatorType'] =
Scholar.CreatorTypes.getName(creators[i]['creatorTypeID']);
}
// Source notes
arr['notes'] = []
}
// Notes
if (this.isNote()){
// Don't need title for notes
delete arr['title'];
arr['note'] = this.getNote();
if (this.getSource()){
arr['sourceItemID'] = this.getSource();
}
}
// If not note, append attached notes
else {
arr['notes'] = [];
var notes = this.getNotes();
for (var i in notes){
var note = Scholar.Items.get(notes[i]);
@ -1207,16 +1460,34 @@ Scholar.Item.prototype.toArray = function(){
}
}
// Notes
else {
// Don't need title for notes
// Append source files
if (this.isFile()){
arr['fileName'] = arr['title'];
delete arr['title'];
arr['note'] = this.getNote();
if (this.getNoteSource()){
arr['sourceItemID'] = this.getNoteSource();
// TODO: file data
if (this.getSource()){
arr['sourceItemID'] = this.getSource();
}
}
// If not file, append attached files
else {
arr['files'] = [];
var files = this.getFiles();
for (var i in files){
var file = Scholar.Items.get(files[i]);
arr['files'].push({
itemID: file.getID(),
// TODO
tags: file.getTags(),
seeAlso: file.getSeeAlso()
});
}
}
arr['tags'] = this.getTags();
arr['seeAlso'] = this.getSeeAlso();
@ -1473,7 +1744,8 @@ Scholar.Items = new function(){
var sql = 'SELECT I.*, lastName || '
+ 'CASE ((SELECT COUNT(*) FROM itemCreators WHERE itemID=I.itemID)>1) '
+ "WHEN 0 THEN '' ELSE ' et al.' END AS firstCreator, "
+ "(SELECT COUNT(*) FROM itemNotes WHERE sourceItemID=I.itemID) AS numNotes "
+ "(SELECT COUNT(*) FROM itemNotes WHERE sourceItemID=I.itemID) AS numNotes, "
+ "(SELECT COUNT(*) FROM itemFiles WHERE sourceItemID=I.itemID) AS numFiles "
+ 'FROM items I '
+ 'LEFT JOIN itemCreators IC ON (I.itemID=IC.itemID) '
+ 'LEFT JOIN creators C ON (IC.creatorID=C.creatorID) '
@ -1554,7 +1826,231 @@ Scholar.Notes = new function(){
Scholar.Files = new function(){
this.LINK_MODE_IMPORTED_FILE = 0;
this.LINK_MODE_IMPORTED_URL = 1;
this.LINK_MODE_LINKED_FILE = 2;
this.LINK_MODE_LINKED_URL = 3;
this.importFromFile = importFromFile;
this.linkFromFile = linkFromFile;
this.linkFromDocument = linkFromDocument;
this.importFromDocument = importFromDocument;
function importFromFile(file, sourceItemID){
var title = file.leafName;
Scholar.DB.beginTransaction();
// Create a new file item
var fileItem = Scholar.Items.getNewItemByType(Scholar.ItemTypes.getID('file'));
fileItem.setField('title', title);
fileItem.save();
var itemID = fileItem.getID();
// Create directory for item files within storage directory
var destDir = Scholar.getStorageDirectory();
destDir.append(itemID);
destDir.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0644);
try {
file.copyTo(destDir, null);
}
catch (e){
// hmph
Scholar.DB.rollbackTransaction();
destDir.remove(true);
throw (e);
}
// Point to copied file
var newFile = Components.classes["@mozilla.org/file/local;1"]
.createInstance(Components.interfaces.nsILocalFile);
newFile.initWithFile(destDir);
newFile.append(title);
var mimeType = _getMIMETypeFromFile(newFile);
var charsetID = _getCharsetIDFromFile(newFile);
_addToDB(newFile, null, null, this.LINK_MODE_IMPORTED_FILE, mimeType, charsetID, sourceItemID, itemID);
Scholar.DB.commitTransaction();
return itemID;
}
function linkFromFile(file, sourceItemID){
var title = file.leafName;
var mimeType = _getMIMETypeFromFile(file);
var charsetID = _getCharsetIDFromFile(file);
return _addToDB(file, null, title, this.LINK_MODE_LINKED_FILE, mimeType, charsetID, sourceItemID);
}
// TODO: what if called on file:// document?
function linkFromDocument(document, sourceItemID){
var url = document.location;
var title = document.title; // TODO: don't use Mozilla-generated title for images, etc.
var mimeType = document.contentType;
var charsetID = Scholar.CharacterSets.getID(document.characterSet);
return _addToDB(null, url, title, this.LINK_MODE_LINKED_URL, mimeType, charsetID, sourceItemID);
}
function importFromDocument(document, sourceItemID){
var url = document.location;
var title = document.title;
var mimeType = document.contentType;
var charsetID = Scholar.CharacterSets.getID(document.characterSet);
const nsIWBP = Components.interfaces.nsIWebBrowserPersist;
var wbp = Components
.classes["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
.createInstance(nsIWBP);
//wbp.persistFlags = nsIWBP.PERSIST_FLAGS...;
var encodingFlags = false;
Scholar.DB.beginTransaction();
// Create a new file item
var fileItem = Scholar.Items.getNewItemByType(Scholar.ItemTypes.getID('file'));
fileItem.setField('title', title);
fileItem.save();
var itemID = fileItem.getID();
// Create a new folder for this item in the storage directory
var destDir = Scholar.getStorageDirectory();
destDir.append(itemID);
destDir.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0644);
var file = Components.classes["@mozilla.org/file/local;1"].
createInstance(Components.interfaces.nsILocalFile);
file.initWithFile(destDir);
file.append(_getFileNameFromURL(url, mimeType));
wbp.saveDocument(document, file, destDir, mimeType, encodingFlags, false);
_addToDB(file, url, title, this.LINK_MODE_IMPORTED_URL, mimeType, charsetID, sourceItemID, itemID);
Scholar.DB.commitTransaction();
return itemID;
}
// TODO: currently only uses file extension
function _getMIMETypeFromFile(file){
var ms = Components
.classes['@mozilla.org/uriloader/external-helper-app-service;1']
.getService(Components.interfaces.nsIMIMEService);
return ms.getTypeFromFile(file);
}
function _getCharsetIDFromFile(file){
// TODO: Not yet implemented
return null;
}
function _getFileNameFromURL(url, mimeType){
var nsIURL = Components.classes["@mozilla.org/network/standard-url;1"]
.createInstance(Components.interfaces.nsIURL);
nsIURL.spec = url;
if (nsIURL.fileName){
return nsIURL.fileName;
}
if (mimeType){
var ext = Components.classes["@mozilla.org/mime;1"]
.getService(Components.interfaces.nsIMIMEService)
.getPrimaryExtension(mimeType, nsIURL.fileExt ? nsIURL.fileExt : null);
}
return nsIURL.host + (ext ? '.' + ext : '');
}
/**
* Create a new item of type 'file' and add the file link to the itemFiles table
*
* Passing an itemID causes it to skip new item creation and use the specified
* item instead -- used when importing files (since we have to know
* the itemID before copying in a file and don't want to update the DB before
* the file is saved)
*
* Returns the itemID of the new file item
**/
function _addToDB(file, url, title, linkMode, mimeType, charsetID, sourceItemID, itemID){
if (url){
var path = url;
}
if (file){
if (linkMode==this.LINK_MODE_IMPORTED_URL){
var originalPath = path;
}
// Path relative to Scholar directory for external files and relative
// to storage directory for imported files
var refDir = (linkMode==this.LINK_MODE_LINKED_FILE)
? Scholar.getScholarDirectory() : Scholar.getStorageDirectory();
var path = file.getRelativeDescriptor(refDir);
}
Scholar.DB.beginTransaction();
if (sourceItemID){
var sourceItem = Scholar.Items.get(sourceItemID);
if (!sourceItem){
Scholar.DB.commitTransaction();
throw ("Cannot set file source to invalid item " + sourceItemID);
}
if (sourceItem.isFile()){
Scholar.DB.commitTransaction();
throw ("Cannot set file source to another file (" + sourceItemID + ")");
}
}
// If an itemID is provided, use that
if (itemID){
var fileItem = Scholar.Items.get(itemID);
if (!fileItem.isFile()){
throw ("Item " + itemID + " is not a valid file item in _addToDB()");
}
}
// Otherwise create a new file item
else {
var fileItem = Scholar.Items.getNewItemByType(Scholar.ItemTypes.getID('file'));
fileItem.setField('title', title);
fileItem.save();
}
var sql = "INSERT INTO itemFiles (itemID, sourceItemID, linkMode, "
+ "mimeType, charsetID, path, originalPath) VALUES (?,?,?,?,?,?,?)";
var bindParams = [
fileItem.getID(),
(sourceItemID ? {int:sourceItemID} : null),
{int:linkMode},
{string:mimeType},
(charsetID ? {int:charsetID} : null),
{string:path},
(originalPath ? {string:originalPath} : null)
];
Scholar.DB.query(sql, bindParams);
Scholar.DB.commitTransaction();
if (sourceItemID){
sourceItem.incrementNoteCount();
Scholar.Notifier.trigger('modify', 'item', sourceItemID);
}
Scholar.Notifier.trigger('add', 'item', fileItem.getID());
return fileItem.getID();
}
}
/*
* Constructor for Collection object
@ -2279,7 +2775,8 @@ Scholar.Tags = new function(){
/*
* Same structure as Scholar.ItemTypes -- make changes in both places if possible
* Same structure as Scholar.ItemTypes and FileTypes --
* make changes in both places if possible
*/
Scholar.CreatorTypes = new function(){
var _types = new Array();
@ -2344,7 +2841,8 @@ Scholar.CreatorTypes = new function(){
/*
* Same structure as Scholar.CreatorTypes -- make changes in both places if possible
* Same structure as Scholar.CreatorTypes and FileTypes --
* make changes in both places if possible
*/
Scholar.ItemTypes = new function(){
var _types = new Array();
@ -2408,6 +2906,162 @@ Scholar.ItemTypes = new function(){
/*
* Same structure as Scholar.ItemTypes and CreatorTypes --
* make changes in both places if possible
*/
Scholar.FileTypes = new function(){
var _types = new Array();
var _typesLoaded;
var self = this;
this.getName = getName;
this.getID = getID;
this.getIDFromMIMEType = getIDFromMIMEType;
this.getTypes = getTypes;
function getName(idOrName){
if (!_typesLoaded){
_load();
}
if (!_types[idOrName]){
Scholar.debug('Invalid file type ' + idOrName, 1);
}
return _types[idOrName]['name'];
}
function getID(idOrName){
if (!_typesLoaded){
_load();
}
if (!_types[idOrName]){
Scholar.debug('Invalid file type ' + idOrName, 1);
}
return _types[idOrName]['id'];
}
function getIDFromMIMEType(mimeType){
// TODO
}
function getTypes(){
return Scholar.DB.query('SELECT fileTypeID AS id, '
+ 'fileType AS name FROM fileTypes order BY fileType');
}
function _load(){
var types = self.getTypes();
for (i in types){
// Store as both id and name for access by either
var typeData = {
id: types[i]['id'],
name: types[i]['name']
}
_types[types[i]['id']] = typeData;
_types[types[i]['name']] = _types[types[i]['id']];
}
_typesLoaded = true;
}
}
/*
* Same structure as Scholar.ItemTypes, CreatorTypes, etc. --
* make changes in all versions if possible
*/
Scholar.CharacterSets = new function(){
var _types = new Array();
var _typesLoaded;
var self = this;
var _typeDesc = 'character set';
var _idCol = 'charsetID';
var _nameCol = 'charset';
var _table = 'charsets';
var _ignoreCase = true;
this.getName = getName;
this.getID = getID;
this.getTypes = getTypes;
this.getAll = getAll;
function getName(idOrName){
if (!_typesLoaded){
_load();
}
if (_ignoreCase){
idOrName = idOrName.toLowerCase();
}
if (!_types[idOrName]){
Scholar.debug('Invalid ' + _typeDesc + ' ' + idOrName, 1);
}
return _types[idOrName]['name'];
}
function getID(idOrName){
if (!_typesLoaded){
_load();
}
if (_ignoreCase){
idOrName = idOrName.toLowerCase();
}
if (!_types[idOrName]){
Scholar.debug('Invalid ' + _typeDesc + ' ' + idOrName, 1);
}
return _types[idOrName]['id'];
}
function getTypes(){
return Scholar.DB.query('SELECT ' + _idCol + ' AS id, '
+ _nameCol + ' AS name FROM ' + _table + ' order BY ' + _nameCol);
}
function getAll(){
return this.getTypes();
}
function _load(){
var types = self.getTypes();
for (i in types){
// Store as both id and name for access by either
var typeData = {
id: types[i]['id'],
name: _ignoreCase ? types[i]['name'].toLowerCase() : types[i]['name']
}
_types[types[i]['id']] = typeData;
_types[types[i]['name']] = _types[types[i]['id']];
}
_typesLoaded = true;
}
}
Scholar.ItemFields = new function(){
// Private members
var _fields = new Array();

View file

@ -236,7 +236,8 @@ Scholar.Schema = new function(){
Scholar.DB.commitTransaction();
}
catch(e){
alert(e);
Scholar.debug(e, 1);
alert('Error initializing Scholar database'); // TODO: localize
Scholar.DB.rollbackTransaction();
}
}
@ -390,7 +391,7 @@ Scholar.Schema = new function(){
//
// Change this value to match the schema version
//
var toVersion = 29;
var toVersion = 30;
if (toVersion != _getSchemaSQLVersion()){
throw('Schema version does not match version in _migrateSchema()');
@ -405,10 +406,18 @@ Scholar.Schema = new function(){
// Each block performs the changes necessary to move from the
// previous revision to that one.
for (var i=fromVersion + 1; i<=toVersion; i++){
if (i==29){
if (i==30){
Scholar.DB.query("DROP TABLE IF EXISTS keywords");
Scholar.DB.query("DROP TABLE IF EXISTS itemKeywords");
Scholar.DB.query("DROP TABLE IF EXISTS scrapers");
// Remove old SQLite DB
var file = Scholar.getProfileDirectory();
file.append('scholar.sqlite');
if (file.exists()){
file.remove(null);
}
_initializeSchema();
}
}

View file

@ -64,6 +64,7 @@ itemTypes.interview = Interview
itemTypes.film = Film
itemTypes.artwork = Artwork
itemTypes.website = Website
itemTypes.file = File
creatorTypes.author = Author
creatorTypes.contributor = Contributor

View file

@ -1,4 +1,4 @@
-- 29
-- 30
DROP TABLE IF EXISTS version;
CREATE TABLE version (
@ -66,11 +66,56 @@
sourceItemID INT,
note TEXT,
PRIMARY KEY (itemID),
FOREIGN KEY (itemID) REFERENCES items(itemID),
FOREIGN KEY (sourceItemID) REFERENCES items(itemID)
);
DROP INDEX IF EXISTS itemNotes_sourceItemID;
CREATE INDEX itemNotes_sourceItemID ON itemNotes(sourceItemID);
DROP TABLE IF EXISTS charsets;
CREATE TABLE charsets (
charsetID INTEGER PRIMARY KEY,
charset TEXT UNIQUE
);
DROP INDEX IF EXISTS charsets_charset;
CREATE INDEX charsets_charset ON charsets(charset);
DROP TABLE IF EXISTS fileTypes;
CREATE TABLE fileTypes (
fileTypeID INTEGER PRIMARY KEY,
fileType TEXT UNIQUE
);
DROP INDEX IF EXISTS fileTypes_fileType;
CREATE INDEX fileTypes_fileType ON fileTypes(fileType);
DROP TABLE IF EXISTS fileTypeMimeTypes;
CREATE TABLE fileTypeMimeTypes (
fileTypeID,
mimeType,
PRIMARY KEY (fileTypeID, mimeType),
FOREIGN KEY (fileTypeID) REFERENCES fileTypes(fileTypeID)
);
DROP INDEX IF EXISTS fileTypeMimeTypes_mimeType;
CREATE INDEX fileTypeMimeTypes_mimeType ON fileTypeMimeTypes(mimeType);
DROP TABLE IF EXISTS itemFiles;
CREATE TABLE itemFiles (
itemID INT,
sourceItemID INT,
linkMode INT,
mimeType TEXT,
charsetID INT,
path TEXT,
originalPath TEXT,
PRIMARY KEY (itemID),
FOREIGN KEY (itemID) REFERENCES items(itemID),
FOREIGN KEY (sourceItemID) REFERENCES items(sourceItemID)
);
DROP INDEX IF EXISTS itemFiles_sourceItemID;
CREATE INDEX itemFiles_sourceItemID ON itemFiles(sourceItemID);
DROP INDEX IF EXISTS itemFiles_mimeType;
CREATE INDEX itemFiles_mimeType ON itemFiles(mimeType);
DROP TABLE IF EXISTS tags;
CREATE TABLE tags (
tagID INT,
@ -201,6 +246,7 @@
INSERT INTO itemTypes VALUES (11,'film');
INSERT INTO itemTypes VALUES (12,'artwork');
INSERT INTO itemTypes VALUES (13,'website');
INSERT INTO itemTypes VALUES (14,'file');
INSERT INTO "fieldFormats" VALUES(1, '.*', 0);
INSERT INTO "fieldFormats" VALUES(2, '[0-9]*', 1);
@ -342,6 +388,175 @@
INSERT INTO "itemTypeFields" VALUES(7, 22, 9);
INSERT INTO "itemTypeFields" VALUES(13, 22, 5);
INSERT INTO "charsets" VALUES(1, 'utf-8');
INSERT INTO "charsets" VALUES(2, 'ascii');
INSERT INTO "charsets" VALUES(3, 'windows-1250');
INSERT INTO "charsets" VALUES(4, 'windows-1251');
INSERT INTO "charsets" VALUES(5, 'windows-1252');
INSERT INTO "charsets" VALUES(6, 'windows-1253');
INSERT INTO "charsets" VALUES(7, 'windows-1254');
INSERT INTO "charsets" VALUES(8, 'windows-1257');
INSERT INTO "charsets" VALUES(9, 'us');
INSERT INTO "charsets" VALUES(10, 'us-ascii');
INSERT INTO "charsets" VALUES(11, 'utf-7');
INSERT INTO "charsets" VALUES(12, 'iso8859-1');
INSERT INTO "charsets" VALUES(13, 'iso8859-15');
INSERT INTO "charsets" VALUES(14, 'iso_646.irv:1991');
INSERT INTO "charsets" VALUES(15, 'iso_8859-1');
INSERT INTO "charsets" VALUES(16, 'iso_8859-1:1987');
INSERT INTO "charsets" VALUES(17, 'iso_8859-2');
INSERT INTO "charsets" VALUES(18, 'iso_8859-2:1987');
INSERT INTO "charsets" VALUES(19, 'iso_8859-4');
INSERT INTO "charsets" VALUES(20, 'iso_8859-4:1988');
INSERT INTO "charsets" VALUES(21, 'iso_8859-5');
INSERT INTO "charsets" VALUES(22, 'iso_8859-5:1988');
INSERT INTO "charsets" VALUES(23, 'iso_8859-7');
INSERT INTO "charsets" VALUES(24, 'iso_8859-7:1987');
INSERT INTO "charsets" VALUES(25, 'iso-8859-1');
INSERT INTO "charsets" VALUES(26, 'iso-8859-1-windows-3.0-latin-1');
INSERT INTO "charsets" VALUES(27, 'iso-8859-1-windows-3.1-latin-1');
INSERT INTO "charsets" VALUES(28, 'iso-8859-15');
INSERT INTO "charsets" VALUES(29, 'iso-8859-2');
INSERT INTO "charsets" VALUES(30, 'iso-8859-2-windows-latin-2');
INSERT INTO "charsets" VALUES(31, 'iso-8859-3');
INSERT INTO "charsets" VALUES(32, 'iso-8859-4');
INSERT INTO "charsets" VALUES(33, 'iso-8859-5');
INSERT INTO "charsets" VALUES(34, 'iso-8859-5-windows-latin-5');
INSERT INTO "charsets" VALUES(35, 'iso-8859-6');
INSERT INTO "charsets" VALUES(36, 'iso-8859-7');
INSERT INTO "charsets" VALUES(37, 'iso-8859-8');
INSERT INTO "charsets" VALUES(38, 'iso-8859-9');
INSERT INTO "charsets" VALUES(39, 'l1');
INSERT INTO "charsets" VALUES(40, 'l2');
INSERT INTO "charsets" VALUES(41, 'l4');
INSERT INTO "charsets" VALUES(42, 'latin1');
INSERT INTO "charsets" VALUES(43, 'latin2');
INSERT INTO "charsets" VALUES(44, 'latin4');
INSERT INTO "charsets" VALUES(45, 'x-mac-ce');
INSERT INTO "charsets" VALUES(46, 'x-mac-cyrillic');
INSERT INTO "charsets" VALUES(47, 'x-mac-greek');
INSERT INTO "charsets" VALUES(48, 'x-mac-roman');
INSERT INTO "charsets" VALUES(49, 'x-mac-turkish');
INSERT INTO "charsets" VALUES(50, 'adobe-symbol-encoding');
INSERT INTO "charsets" VALUES(51, 'ansi_x3.4-1968');
INSERT INTO "charsets" VALUES(52, 'ansi_x3.4-1986');
INSERT INTO "charsets" VALUES(53, 'big5');
INSERT INTO "charsets" VALUES(54, 'chinese');
INSERT INTO "charsets" VALUES(55, 'cn-big5');
INSERT INTO "charsets" VALUES(56, 'cn-gb');
INSERT INTO "charsets" VALUES(57, 'cn-gb-isoir165');
INSERT INTO "charsets" VALUES(58, 'cp367');
INSERT INTO "charsets" VALUES(59, 'cp819');
INSERT INTO "charsets" VALUES(60, 'cp850');
INSERT INTO "charsets" VALUES(61, 'cp852');
INSERT INTO "charsets" VALUES(62, 'cp855');
INSERT INTO "charsets" VALUES(63, 'cp857');
INSERT INTO "charsets" VALUES(64, 'cp862');
INSERT INTO "charsets" VALUES(65, 'cp864');
INSERT INTO "charsets" VALUES(66, 'cp866');
INSERT INTO "charsets" VALUES(67, 'csascii');
INSERT INTO "charsets" VALUES(68, 'csbig5');
INSERT INTO "charsets" VALUES(69, 'cseuckr');
INSERT INTO "charsets" VALUES(70, 'cseucpkdfmtjapanese');
INSERT INTO "charsets" VALUES(71, 'csgb2312');
INSERT INTO "charsets" VALUES(72, 'cshalfwidthkatakana');
INSERT INTO "charsets" VALUES(73, 'cshppsmath');
INSERT INTO "charsets" VALUES(74, 'csiso103t618bit');
INSERT INTO "charsets" VALUES(75, 'csiso159jisx02121990');
INSERT INTO "charsets" VALUES(76, 'csiso2022jp');
INSERT INTO "charsets" VALUES(77, 'csiso2022jp2');
INSERT INTO "charsets" VALUES(78, 'csiso2022kr');
INSERT INTO "charsets" VALUES(79, 'csiso58gb231280');
INSERT INTO "charsets" VALUES(80, 'csisolatin4');
INSERT INTO "charsets" VALUES(81, 'csisolatincyrillic');
INSERT INTO "charsets" VALUES(82, 'csisolatingreek');
INSERT INTO "charsets" VALUES(83, 'cskoi8r');
INSERT INTO "charsets" VALUES(84, 'csksc56011987');
INSERT INTO "charsets" VALUES(85, 'csshiftjis');
INSERT INTO "charsets" VALUES(86, 'csunicode11');
INSERT INTO "charsets" VALUES(87, 'csunicode11utf7');
INSERT INTO "charsets" VALUES(88, 'csunicodeascii');
INSERT INTO "charsets" VALUES(89, 'csunicodelatin1');
INSERT INTO "charsets" VALUES(90, 'cswindows31latin5');
INSERT INTO "charsets" VALUES(91, 'cyrillic');
INSERT INTO "charsets" VALUES(92, 'ecma-118');
INSERT INTO "charsets" VALUES(93, 'elot_928');
INSERT INTO "charsets" VALUES(94, 'euc-jp');
INSERT INTO "charsets" VALUES(95, 'euc-kr');
INSERT INTO "charsets" VALUES(96, 'extended_unix_code_packed_format_for_japanese');
INSERT INTO "charsets" VALUES(97, 'gb2312');
INSERT INTO "charsets" VALUES(98, 'gb_2312-80');
INSERT INTO "charsets" VALUES(99, 'greek');
INSERT INTO "charsets" VALUES(100, 'greek8');
INSERT INTO "charsets" VALUES(101, 'hz-gb-2312');
INSERT INTO "charsets" VALUES(102, 'ibm367');
INSERT INTO "charsets" VALUES(103, 'ibm819');
INSERT INTO "charsets" VALUES(104, 'ibm850');
INSERT INTO "charsets" VALUES(105, 'ibm852');
INSERT INTO "charsets" VALUES(106, 'ibm855');
INSERT INTO "charsets" VALUES(107, 'ibm857');
INSERT INTO "charsets" VALUES(108, 'ibm862');
INSERT INTO "charsets" VALUES(109, 'ibm864');
INSERT INTO "charsets" VALUES(110, 'ibm866');
INSERT INTO "charsets" VALUES(111, 'iso-10646');
INSERT INTO "charsets" VALUES(112, 'iso-10646-j-1');
INSERT INTO "charsets" VALUES(113, 'iso-10646-ucs-2');
INSERT INTO "charsets" VALUES(114, 'iso-10646-ucs-4');
INSERT INTO "charsets" VALUES(115, 'iso-10646-ucs-basic');
INSERT INTO "charsets" VALUES(116, 'iso-10646-unicode-latin1');
INSERT INTO "charsets" VALUES(117, 'iso-2022-jp');
INSERT INTO "charsets" VALUES(118, 'iso-2022-jp-2');
INSERT INTO "charsets" VALUES(119, 'iso-2022-kr');
INSERT INTO "charsets" VALUES(120, 'iso-ir-100');
INSERT INTO "charsets" VALUES(121, 'iso-ir-101');
INSERT INTO "charsets" VALUES(122, 'iso-ir-103');
INSERT INTO "charsets" VALUES(123, 'iso-ir-110');
INSERT INTO "charsets" VALUES(124, 'iso-ir-126');
INSERT INTO "charsets" VALUES(125, 'iso-ir-144');
INSERT INTO "charsets" VALUES(126, 'iso-ir-149');
INSERT INTO "charsets" VALUES(127, 'iso-ir-159');
INSERT INTO "charsets" VALUES(128, 'iso-ir-58');
INSERT INTO "charsets" VALUES(129, 'iso-ir-6');
INSERT INTO "charsets" VALUES(130, 'iso646-us');
INSERT INTO "charsets" VALUES(131, 'jis_x0201');
INSERT INTO "charsets" VALUES(132, 'jis_x0208-1983');
INSERT INTO "charsets" VALUES(133, 'jis_x0212-1990');
INSERT INTO "charsets" VALUES(134, 'koi8-r');
INSERT INTO "charsets" VALUES(135, 'korean');
INSERT INTO "charsets" VALUES(136, 'ks_c_5601');
INSERT INTO "charsets" VALUES(137, 'ks_c_5601-1987');
INSERT INTO "charsets" VALUES(138, 'ks_c_5601-1989');
INSERT INTO "charsets" VALUES(139, 'ksc5601');
INSERT INTO "charsets" VALUES(140, 'ksc_5601');
INSERT INTO "charsets" VALUES(141, 'ms_kanji');
INSERT INTO "charsets" VALUES(142, 'shift_jis');
INSERT INTO "charsets" VALUES(143, 't.61');
INSERT INTO "charsets" VALUES(144, 't.61-8bit');
INSERT INTO "charsets" VALUES(145, 'unicode-1-1-utf-7');
INSERT INTO "charsets" VALUES(146, 'unicode-1-1-utf-8');
INSERT INTO "charsets" VALUES(147, 'unicode-2-0-utf-7');
INSERT INTO "charsets" VALUES(148, 'windows-31j');
INSERT INTO "charsets" VALUES(149, 'x-cns11643-1');
INSERT INTO "charsets" VALUES(150, 'x-cns11643-1110');
INSERT INTO "charsets" VALUES(151, 'x-cns11643-2');
INSERT INTO "charsets" VALUES(152, 'x-cp1250');
INSERT INTO "charsets" VALUES(153, 'x-cp1251');
INSERT INTO "charsets" VALUES(154, 'x-cp1253');
INSERT INTO "charsets" VALUES(155, 'x-dectech');
INSERT INTO "charsets" VALUES(156, 'x-dingbats');
INSERT INTO "charsets" VALUES(157, 'x-euc-jp');
INSERT INTO "charsets" VALUES(158, 'x-euc-tw');
INSERT INTO "charsets" VALUES(159, 'x-gb2312-11');
INSERT INTO "charsets" VALUES(160, 'x-imap4-modified-utf7');
INSERT INTO "charsets" VALUES(161, 'x-jisx0208-11');
INSERT INTO "charsets" VALUES(162, 'x-ksc5601-11');
INSERT INTO "charsets" VALUES(163, 'x-sjis');
INSERT INTO "charsets" VALUES(164, 'x-tis620');
INSERT INTO "charsets" VALUES(165, 'x-unicode-2-0-utf-7');
INSERT INTO "charsets" VALUES(166, 'x-x-big5');
INSERT INTO "charsets" VALUES(167, 'x0201');
INSERT INTO "charsets" VALUES(168, 'x0212');
-- Some sample data
INSERT INTO "items" VALUES(1, 2, 'Online connections: Internet interpersonal relationships', '2006-03-12 05:24:40', '2006-03-12 05:24:40');
INSERT INTO "items" VALUES(2, 2, 'Computer-Mediated Communication: Human-to-Human Communication Across the Internet', '2006-03-12 05:25:50', '2006-03-12 05:25:50');
@ -411,6 +626,15 @@
INSERT INTO "itemNotes" VALUES(16, 1, 'This item is note-worthy.');
INSERT INTO "itemNotes" VALUES(17, NULL, 'This is an independent note.');
INSERT INTO "fileTypes" VALUES(1, 'link');
INSERT INTO "fileTypes" VALUES(2, 'snapshot');
INSERT INTO "fileTypes" VALUES(3, 'image');
INSERT INTO "fileTypes" VALUES(4, 'pdf');
INSERT INTO "fileTypes" VALUES(5, 'audio');
INSERT INTO "fileTypes" VALUES(6, 'video');
INSERT INTO "fileTypes" VALUES(7, 'document');
INSERT INTO "fileTypes" VALUES(8, 'presentation');
INSERT INTO collections VALUES (1241, 'Test Project', NULL);
INSERT INTO collections VALUES (3262, 'Another Test Project', NULL);
INSERT INTO collections VALUES (6856, 'Yet Another Project', NULL);