Initial import of repository structure, basic extension layout, and functions for DB abstraction and schema maintenance

This commit is contained in:
Dan Stillman 2006-02-21 17:01:06 +00:00
commit 9f38337ec7
10 changed files with 452 additions and 0 deletions

6
chrome.manifest Normal file
View file

@ -0,0 +1,6 @@
content scholar chrome/chromeFiles/content/scholar/
locale scholar en-US chrome/chromeFiles/locale/en-US/scholar/
skin scholar default chrome/chromeFiles/skin/default/scholar/
overlay chrome://browser/content/browser.xul chrome://scholar/content/scholar.xul
style chrome://browser/content/browser.xul chrome://scholar/skin/scholar.css

View file

@ -0,0 +1,257 @@
var scholarDB = new Scholar_DB();
/*
* DB connection and schema management class
*/
function Scholar_DB(){
// Private members
var _connection;
// Privileged methods
this.query = query;
this.valueQuery = valueQuery;
this.rowQuery = rowQuery;
this.statementQuery = statementQuery;
this.updateSchema = updateSchema;
/////////////////////////////////////////////////////////////////
//
// Privileged methods
//
/////////////////////////////////////////////////////////////////
/*
* Run an SQL query
*
* Returns:
* - mozIStorageStatementWrapper for SELECT's
* - lastInsertId for INSERT's
* - TRUE for other successful queries
* - FALSE on error
*/
function query(sql){
var db = _getDBConnection();
try {
// Parse out the SQL command being used
var op = sql.match(/^[^a-z]*[^ ]+/i).toString().toLowerCase();
// If SELECT statement, return result
if (op=='select'){
var wrapper =
Components.classes['@mozilla.org/storage/statement-wrapper;1']
.createInstance(Components.interfaces.mozIStorageStatementWrapper);
wrapper.initialize(db.createStatement(sql));
return wrapper;
}
else {
db.executeSimpleSQL(sql);
if (op=='insert'){
return db.lastInsertId;
}
// DEBUG: Can't get affected rows for UPDATE or DELETE?
else {
return true;
}
}
}
catch(ex){
alert(db.lastErrorString);
return false;
}
}
/*
* Query a single value and return it
*/
function valueQuery(sql){
var db = _getDBConnection();
try {
var statement = db.createStatement(sql);
}
catch (e){
alert(db.lastErrorString);
return false;
}
// No rows
if (!statement.executeStep()){
return false;
}
var value = statement.getAsUTF8String(0);
statement.reset();
return value;
}
/*
* Run a query and return the first row
*/
function rowQuery(sql){
var result = query(sql);
if (result && result.step()){
return result.row;
}
}
/*
* Run a query, returning a mozIStorageStatement for direct manipulation
*/
function statementQuery(sql){
var db = _getDBConnection();
try {
return db.createStatement(sql);
}
catch (e){
return false;
}
}
/*
* Checks if the DB schema exists and is up-to-date, updating if necessary
*/
function updateSchema(){
var DBVersion = _getDBVersion();
if (DBVersion > SCHOLAR_CONFIG['DB_VERSION']){
throw("Scholar DB version is newer than config version");
}
else if (DBVersion < SCHOLAR_CONFIG['DB_VERSION']){
if (!DBVersion){
dump('Database does not exist -- creating\n');
return _initializeSchema();
}
return _migrateSchema(DBVersion);
}
}
/////////////////////////////////////////////////////////////////
//
// Private methods
//
/////////////////////////////////////////////////////////////////
/*
* Retrieve a link to the data store
*/
function _getDBConnection(){
if (_connection){
return _connection;
}
// Get the storage service
var store = Components.classes["@mozilla.org/storage/service;1"].
getService(Components.interfaces.mozIStorageService);
// Get the profile directory
var file = Components.classes["@mozilla.org/file/directory_service;1"]
.getService(Components.interfaces.nsIProperties)
.get("ProfD", Components.interfaces.nsILocalFile);
// This makes file point to PROFILE_DIR/<scholar database file>
file.append(SCHOLAR_CONFIG['DB_FILE']);
_connection = store.openDatabase(file);
return _connection;
}
/*
* Retrieve the DB schema version
*/
function _getDBVersion(){
if (_getDBConnection().tableExists('version')){
return valueQuery("SELECT version FROM version;");
}
return false;
}
/*
* Load in SQL schema
*/
function _getSchemaSQL(){
// We pull the schema from an external file so we only have to process
// it when necessary
var req = new XMLHttpRequest();
req.open("GET", "chrome://scholar/content/schema.xml", false);
req.send(null);
var schemaVersion =
req.responseXML.documentElement.getAttribute('version');
if (schemaVersion!=SCHOLAR_CONFIG['DB_VERSION']){
throw("Scholar config version does not match schema version");
}
return req.responseXML.documentElement.firstChild.data;
}
/*
* Retrieve the version attribute of the schema SQL XML
*/
function _getSchemaSQLVersion(){
var req = new XMLHttpRequest();
req.open("GET", "chrome://scholar/content/schema.xml", false);
req.send(null);
return req.responseXML.documentElement.getAttribute('version');
}
/*
* Create new DB schema
*/
function _initializeSchema(){
query(_getSchemaSQL());
query("INSERT INTO version VALUES (" + SCHOLAR_CONFIG['DB_VERSION'] + ")");
}
/*
* Migrate schema from an older version, preserving data
*/
function _migrateSchema(fromVersion){
var toVersion = SCHOLAR_CONFIG['DB_VERSION'];
var schemaVersion = _getSchemaSQLVersion();
if (toVersion!=schemaVersion){
throw("Scholar config version does not match schema version");
}
dump('Updating DB from version ' + fromVersion + ' to ' + toVersion + '\n');
// Step through version changes until we reach the current version
//
// Each block performs the changes necessary to move from the
// previous revision to that one.
//
// N.B. Be sure to call _updateDBVersion(i) at the end of each block!
for (var i=fromVersion+1; i<=toVersion; i++){
if (i==1){
// do stuff
// _updateDBVersion(i);
}
}
}
/*
* Update the DB schema version tag of an existing database
*/
function _updateDBVersion(version){
return query("UPDATE version SET version=" + version);
}
}

View file

@ -0,0 +1,85 @@
<schema version="1">
BEGIN;
CREATE TABLE version (
version INT PRIMARY KEY
);
CREATE TABLE objects (
objectID INT PRIMARY KEY,
objectTypeID INT,
objectTitle TEXT,
objectDate DATETIME,
objectDateAdded DATETIME DEFAULT CURRENT_TIMESTAMP,
objectDateModified DATETIME DEFAULT CURRENT_TIMESTAMP,
objectSource TEXT,
objectRights TEXT,
parentID INT,
orderIndex INT
);
CREATE INDEX parentID ON objects (parentID);
CREATE TABLE objectTypes (
objectTypeID INT PRIMARY KEY,
typeName TEXT
);
CREATE TABLE fields (
fieldID INT PRIMARY KEY,
fieldName TEXT,
regex TEXT
);
CREATE TABLE objectTypeFields (
objectTypeID,
fieldID INT,
orderIndex INT,
PRIMARY KEY (objectTypeID, fieldID),
FOREIGN KEY (objectTypeID) REFERENCES objectTypes(objectTypeID),
FOREIGN KEY (fieldID) REFERENCES objectTypes(objectTypeID)
);
CREATE TABLE objectData (
objectID INT,
fieldID INT,
value NONE,
PRIMARY KEY (objectID, fieldID),
FOREIGN KEY (objectID) REFERENCES objects(objectID),
FOREIGN KEY (fieldID) REFERENCES fields(fieldID)
);
CREATE INDEX value ON objectData (value);
CREATE TABLE keywords (
keywordID INT PRIMARY KEY,
keyword TEXT
);
CREATE TABLE objectKeywords (
objectID INT,
keywordID INT,
PRIMARY KEY (objectID, keywordID),
FOREIGN KEY (objectID) REFERENCES objects(objectID),
FOREIGN KEY (keywordID) REFERENCES keywords(keywordID)
);
CREATE TABLE creators (
creatorID INT PRIMARY KEY,
firstName TEXT,
lastName TEXT
);
CREATE TABLE creatorTypes (
creatorTypeID INT PRIMARY KEY,
creatorType TEXT
);
CREATE TABLE objectCreators (
objectID INT,
creatorID INT,
creatorTypeID INT,
orderIndex INT,
PRIMARY KEY (objectID, creatorID),
FOREIGN KEY (objectID) REFERENCES objects(objectID),
FOREIGN KEY (creatorID) REFERENCES creators(creatorID)
);
COMMIT;
</schema>

View file

@ -0,0 +1,53 @@
const SCHOLAR_CONFIG = {
DB_FILE : 'scholar.sdb',
DB_VERSION : 1,
DEBUG_LOGGING : true
};
/*
* Core functions
*/
var Scholar = {
/*
* Initialize the extension
*/
init: function() {
scholarDB.updateSchema();
},
/*
* Debug logging function
*
* Uses DebugLogger extension available from http://mozmonkey.com/debuglogger/
* if available, otherwise the console
*
* Defaults to log level 3 if level not provided
*/
debug: function(message, level) {
if (!SCHOLAR_CONFIG['DEBUG_LOGGING']){
return false;
}
if (!level){
level = 3;
}
try {
var logManager =
Components.classes["@mozmonkey.com/debuglogger/manager;1"]
.getService(Components.interfaces.nsIDebugLoggerManager);
var logger = logManager.registerLogger("Firefox Scholar");
}
catch (e){}
if (logger){
logger.log(level, message);
}
else {
dump('scholar(' + level + '): ' + message);
}
return true;
}
}
window.addEventListener("load", function(e) { Scholar.init(e); }, false);

View file

@ -0,0 +1,14 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://scholar/skin/scholar.css" type="text/css"?>
<!DOCTYPE window SYSTEM "chrome://scholar/locale/scholar.dtd">
<overlay id="scholar"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script src="scholar.js"/>
<script src="db.js"/>
<statusbar id="status-bar">
<statusbarpanel id="my-panel" label="&statusbarpanel.helloworld;"/>
</statusbar>
</overlay>

View file

@ -0,0 +1 @@
alert("Hello, world!");

View file

@ -0,0 +1,18 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://scholar/skin/scholar.css" type="text/css"?>
<!DOCTYPE window SYSTEM "chrome://scholar/locale/scholar.dtd">
<window
id="test-window"
title="Test"
orient="horizontal"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
style="padding:2em">
<script src="../scholar.js"/>
<script src="../db.js"/>
<script src="test.js"/>
<label>I am a test page.</label>
</window>

View file

@ -0,0 +1 @@
<!ENTITY statusbarpanel.helloworld "Hello, World">

17
install.rdf Normal file
View file

@ -0,0 +1,17 @@
<?xml version="1.0"?>
<RDF:RDF xmlns:em="http://www.mozilla.org/2004/em-rdf#"
xmlns:NC="http://home.netscape.com/NC-rdf#"
xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<RDF:Description RDF:about="rdf:#$V4RG2"
em:id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"
em:minVersion="1.0+"
em:maxVersion="1.5.0.*" />
<RDF:Description RDF:about="urn:mozilla:install-manifest"
em:id="scholar@chnm"
em:name="Firefox Scholar"
em:version="1.0"
em:creator="Center for History and New Media, George Mason University"
em:homepageURL="http://chnm.gmu.edu">
<em:targetApplication RDF:resource="rdf:#$V4RG2"/>
</RDF:Description>
</RDF:RDF>