From 42e58f6a68be35a18c13793d83b9b66aeedb1e41 Mon Sep 17 00:00:00 2001 From: Tony Anderson Date: Tue, 9 Sep 2008 11:08:30 +0200 Subject: [PATCH] initial import --- buildmanifest.py | 74 ++++++++++++++ fetch_db.js | 92 +++++++++++++++++ fetch_db.php | 67 ++++++++++++ gears_init.js | 87 ++++++++++++++++ go_offline.html | 98 ++++++++++++++++++ go_offline.js | 100 ++++++++++++++++++ offlinemanifest.json | 274 ++++++++++++++++++++++++++++++++++++++++++++++++++ offlinemoodle.js | 139 +++++++++++++++++++++++++ offlinemoodle.user.js | 176 ++++++++++++++++++++++++++++++++ readme | 91 +++++++++++++++++ 10 files changed, 1198 insertions(+) create mode 100755 buildmanifest.py create mode 100644 fetch_db.js create mode 100644 fetch_db.php create mode 100755 gears_init.js create mode 100644 go_offline.html create mode 100644 go_offline.js create mode 100644 offlinemanifest.json create mode 100755 offlinemoodle.js create mode 100644 offlinemoodle.user.js create mode 100644 readme diff --git a/buildmanifest.py b/buildmanifest.py new file mode 100755 index 0000000..fd56ab0 --- /dev/null +++ b/buildmanifest.py @@ -0,0 +1,74 @@ +OUTPUT = ['../', 'testsite1'] +PREFIX = [ +'{', +' "betaManifestVersion": 1,', +' "version": "version 1.0",', +' "entries": [' +] + +SUFFIX = [ +' ]', +'}' +] + +try: + import sys + import os + import path + import urllib2 + from BeautifulSoup import BeautifulSoup +except: + print 'import failure' + +#open manifest.json in OUTPUT for write +temp = path.path(OUTPUT[0]) +output = temp.joinpath(OUTPUT[1]) +absout = os.path.abspath(output) +print 'absout', absout +manifestname = output.joinpath('manifest.json') +manifest = open(manifestname, 'w') +for outlines in PREFIX: + manifest.write(outlines) + manifest.write('\n') +infile = open('filelist.txt') +filelist = infile.readlines() +count = 1 +for filename in filelist: + page = urllib2.urlopen(filename) + soup = BeautifulSoup(page) + temp = soup.prettify() + temp1 = temp.replace('notloggedin', '') + pretty = temp1.replace('moodle', 'testsite1') + n = filename.find('moodle') + page = path.path(filename[n+7:-1]) + print 'page', page + fldrs = page.splitall() + print 'fldrs =', fldrs + for i in range(1, len(fldrs) - 1): + if not os.path.isdir(fldrs[i]): + print 'mkdir', fldrs[i] + os.mkdir(fldrs[i]) + os.chdir(fldrs[i]) + os.chdir(absout) + temp = page.replace('.php', '') + realpage = temp.replace('?id=','') + '.html' + print 'page', page, 'realpage', realpage + try: + outfile = open(realpage, 'w') + print 'realpage', realpage + outfile.write(pretty) + outfile.close() + except: + print 'output error' + s = ' { "url": "' + realpage + '" }' + manifest.write(s) + if count < len(filelist): + manifest.write(',\n') + else: + manifest.write('\n') + count += 1 +for outlines in SUFFIX: + manifest.write(outlines) + manifest.write('\n') +manifest.close() +sys.exit() diff --git a/fetch_db.js b/fetch_db.js new file mode 100644 index 0000000..7ca5f25 --- /dev/null +++ b/fetch_db.js @@ -0,0 +1,92 @@ + +function fetchDb(){ + // Create a function that will receive data sent from the server + ajaxRequest = new XMLHttpRequest(); + ajaxRequest.onreadystatechange = function(){ + if(ajaxRequest.readyState == 4){ + document.getElementById("dbresult").innerHTML = ajaxRequest.responseText; + alert('fetch done'); + } + } + var queryString = "?SELECT id, lessonid, pageid, jumpto, answer, response FROM mdl_lesson_answers"; + ajaxRequest.open("GET", "fetch_db.php" + queryString, true); + ajaxRequest.send(null); +} +function cacheDb(){ + // Create a function that will copy data sent from the server to a gears db + // Datbase Setup + var cell = new Array(6) + db.open(DB_NAME); + db.execute('drop table if exists mdl_lesson_answers'); + db.execute('create table if not exists mdl_lesson_answers ' + '(id int, lessonid int, pageid int, jumpto int, answer text, response text)'); + var mytbl = document.getElementById('mdl_lesson_answers') + if (mytbl) { + for (var i = 1; i < document.getElementById('mdl_lesson_answers').rows.length; i++) { + for (var j = 0; j < document.getElementById('mdl_lesson_answers').rows[i].cells.length; j++) { + cell[j] = document.getElementById('mdl_lesson_answers').rows[i].cells[j].innerHTML; + } + db.execute('insert into mdl_lesson_answers values (?, ?, ?, ?, ?, ?)', [cell[0], cell[1], cell[2], cell[3], cell[4], cell[5]]); + } + } + db.execute('drop table if exists mdl_lesson_pages'); + db.execute('create table if not exists mdl_lesson_pages ' + '(id int, nextpageid int)'); + var mytbl = document.getElementById('mdl_lesson_pages') + if (mytbl) { + for (var i = 1; i < document.getElementById('mdl_lesson_pages').rows.length; i++) { + for (var j = 0; j < document.getElementById('mdl_lesson_pages').rows[i].cells.length; j++) { + cell[j] = document.getElementById('mdl_lesson_pages').rows[i].cells[j].innerHTML; + } + db.execute('insert into mdl_lesson_pages values (?, ?)', [cell[0], cell[1]]); + } + } + alert('cache done: ' + db.lastInsertRowId); + db.close(); +} + +function displayDb() { + db.open(DB_NAME); + var rslt = ""; + var rw = ""; + rw = ""; + rs = db.execute( 'select id, lessonid, pageid, jumpto, answer, response from mdl_lesson_answers'); + for (var i=0; i" + rs.fieldName(i) + ""; + } + rslt = rslt + rw + ""; + while (rs.isValidRow()) + { + rw = ""; + for (var i=0; i" + rs.field(i) + ""; + } + rslt = rslt + rw + ""; + rs.next(); + } + rslt = rslt + "
"; + + // display mdl_lessons_pages + rslt = rslt + ""; + rw = ""; + rs = db.execute( 'select id, nextpageid from mdl_lesson_pages'); + for (var i=0; i" + rs.fieldName(i) + ""; + } + rslt = rslt + rw + ""; + while (rs.isValidRow()) + { + rw = ""; + for (var i=0; i" + rs.field(i) + ""; + } + rslt = rslt + rw + ""; + rs.next(); + } + rslt = rslt + "
"; + document.getElementById("dbdisplay").innerHTML = rslt; + rs.close(); + db.close(); +} diff --git a/fetch_db.php b/fetch_db.php new file mode 100644 index 0000000..8315879 --- /dev/null +++ b/fetch_db.php @@ -0,0 +1,67 @@ +"; +echo "

Table: {$table}

"; +echo ""; +// printing table headers +for($i=0; $i<$fields_num; $i++) +{ + $field = mysql_fetch_field($result); + echo ""; +} +echo "\n"; +// printing table rows +while($row = mysql_fetch_row($result)) +{ + echo ""; + + // $row is array... foreach( .. ) puts every element + // of $row to $cell variable + foreach($row as $cell) + echo ""; + + echo "\n"; +} +//echo ""; +mysql_free_result($result); + +// Retrieve data from the lesson "pages" table +$result = mysql_query("SELECT id, nextpageid FROM mdl_lesson_pages") +or die(mysql_error()); + +$fields_num = mysql_num_fields($result); + +//echo "
{$field->name}
$cell
"; +// printing table headers +for($i=0; $i<$fields_num; $i++) +{ + $field = mysql_fetch_field($result); + echo ""; +} +echo "\n"; +// printing table rows +while($row = mysql_fetch_row($result)) +{ + echo ""; + + // $row is array... foreach( .. ) puts every element + // of $row to $cell variable + foreach($row as $cell) + echo ""; + + echo "\n"; +} +//echo ""; +mysql_free_result($result); +?> diff --git a/gears_init.js b/gears_init.js new file mode 100755 index 0000000..fab1a8b --- /dev/null +++ b/gears_init.js @@ -0,0 +1,87 @@ +// Copyright 2007, Google Inc. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// 3. Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Sets up google.gears.*, which is *the only* supported way to access Gears. +// +// Circumvent this file at your own risk! +// +// In the future, Gears may automatically define google.gears.* without this +// file. Gears may use these objects to transparently fix bugs and compatibility +// issues. Applications that use the code below will continue to work seamlessly +// when that happens. + +(function() { + // We are already defined. Hooray! + if (window.google && google.gears) { + return; + } + + var factory = null; + + // Firefox + if (typeof GearsFactory != 'undefined') { + factory = new GearsFactory(); + } else { + // IE + try { + factory = new ActiveXObject('Gears.Factory'); + // privateSetGlobalObject is only required and supported on WinCE. + if (factory.getBuildInfo().indexOf('ie_mobile') != -1) { + factory.privateSetGlobalObject(this); + } + } catch (e) { + // Safari + if ((typeof navigator.mimeTypes != 'undefined') + && navigator.mimeTypes["application/x-googlegears"]) { + factory = document.createElement("object"); + factory.style.display = "none"; + factory.width = 0; + factory.height = 0; + factory.type = "application/x-googlegears"; + document.documentElement.appendChild(factory); + } + } + } + + // *Do not* define any objects if Gears is not installed. This mimics the + // behavior of Gears defining the objects in the future. + if (!factory) { + return; + } + + // Now set up the objects, being careful not to overwrite anything. + // + // Note: In Internet Explorer for Windows Mobile, you can't add properties to + // the window object. However, global objects are automatically added as + // properties of the window object in all browsers. + if (!window.google) { + google = {}; + } + + if (!google.gears) { + google.gears = {factory: factory}; + } +})(); + diff --git a/go_offline.html b/go_offline.html new file mode 100644 index 0000000..43b5d07 --- /dev/null +++ b/go_offline.html @@ -0,0 +1,98 @@ + + + + + + + + + + + +Enable Offline Usage + + + + +

Getting offline-enabled documents with Gears

+

 

+
+

Status Message:

+
+ + +

Q: I want to see these documents when I'm not online! What must I do?
+A: Install Gears on your computer and then click "Capture" to store the documents to your computer. You can then access the URLs without a network connection.

+ +

+ +

+ + +

Q: I want to remove my offline access to these documents. What must I do?
+A: Click "Erase" below to removed the "captured" documents from your computer. The documents will no longer be available without a network connection.

+ +

+ +

+ +

+ +

+ +

+ +

+ +

+ +

+ + + + + +
+ + + + diff --git a/go_offline.js b/go_offline.js new file mode 100644 index 0000000..4109a0f --- /dev/null +++ b/go_offline.js @@ -0,0 +1,100 @@ +// Copyright 2007, Google Inc. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// 1. Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// 3. Neither the name of Google Inc. nor the names of its contributors may be +// used to endorse or promote products derived from this software without +// specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED +// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF +// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Change this to set the name of the managed resource store to create. +// You use the name with the createManagedStore, and removeManagedStore, +// and openManagedStore APIs. It isn't visible to the user. +var STORE_NAME = "my_offline_docset"; +var DB_NAME = "mdl_offline_db"; + +// Change this to set the URL of tha manifest file, which describe which +// URLs to capture. It can be relative to the current page, or an +// absolute URL. +var MANIFEST_FILENAME = "offlinemanifest.json"; + +var localServer; +var store; +var db; + +// Called onload to initialize local server, store variables, and local database +function init() { + if (!window.google || !google.gears) { + textOut("NOTE: You must install Gears first."); + } else { + localServer = google.gears.factory.create("beta.localserver"); + store = localServer.createManagedStore(STORE_NAME); + db = google.gears.factory.create('beta.database', '1.0'); + textOut("Yeay, Gears is already installed."); + } +} + +// Create the managed resource store +function createStore() { + if (!window.google || !google.gears) { + alert("You must install Gears first."); + return; + } + + store.manifestUrl = MANIFEST_FILENAME; + store.checkForUpdate(); + + var timerId = window.setInterval(function() { + // When the currentVersion property has a value, all of the resources + // listed in the manifest file for that version are captured. There is + // an open bug to surface this state change as an event. + if (store.currentVersion) { + window.clearInterval(timerId); + textOut("The documents are now available offline.\n" + + "With your browser offline, load the document at " + + "its normal online URL to see the locally stored " + + "version. The version stored is: " + + store.currentVersion); + } else if (store.updateStatus == 3) { + textOut("Error: " + store.lastErrorMessage); + } + }, 500); +} + +// Remove the managed resource store. +function removeStore() { + if (!window.google || !google.gears) { + alert("You must install Gears first."); + return; + } + + localServer.removeManagedStore(STORE_NAME); + textOut("Done. The local store has been removed." + + "You will now see online versions of the documents."); +} + +// Utility function to output some status text. +function textOut(s) { + var elm = document.getElementById("textOut"); + while (elm.firstChild) { + elm.removeChild(elm.firstChild); + } + elm.appendChild(document.createTextNode(s)); +} + diff --git a/offlinemanifest.json b/offlinemanifest.json new file mode 100644 index 0000000..60264a1 --- /dev/null +++ b/offlinemanifest.json @@ -0,0 +1,274 @@ +{ + "betaManifestVersion": 1, + "version": "version 2.0", + "entries": [ + { "url" : "lib/offline/offlinemoodle.js" }, + { "url" : "go_offline.html" }, + { "url" : "testpage.html" }, + { "url" : "go_offline.js" }, + { "url" : "gears_init.js" }, + { "url" : "lib/javascript-static.js" }, + { "url" : "lib/javascript-mod.php" }, + { "url" : "lib/overlib/overlib.js" }, + { "url" : "lib/overlib/overlib_cssstyle.js" }, + { "url" : "lib/cookies.js" }, + { "url" : "lib/ufo.js" }, + { "url" : "lib/dropdown.js" }, + { "url" : "pix/help.gif" }, + { "url" : "pix/i/users.gif" }, + { "url" : "mod/assignment/icon.gif" }, + { "url" : "mod/chat/icon.gif" }, + { "url" : "mod/choice/icon.gif" }, + { "url" : "mod/forum/icon.gif" }, + { "url" : "mod/glossary/icon.gif" }, + { "url" : "mod/lesson/icon.gif" }, + { "url" : "mod/quiz/icon.gif" }, + { "url" : "mod/resource/icon.gif" }, + { "url" : "mod/scorm/icon.gif" }, + { "url" : "mod/survey/icon.gif" }, + { "url" : "mod/wiki/icon.gif" }, + { "url" : "pix/help.gif" }, + { "url" : "pix/i/edit.gif" }, + { "url" : "pix/i/settings.gif" }, + { "url" : "pix/i/roles.gif" }, + { "url" : "pix/i/grades.gif" }, + { "url" : "pix/i/backup.gif" }, + { "url" : "pix/i/restore.gif" }, + { "url" : "pix/i/restore.gif" }, + { "url" : "pix/i/return.gif" }, + { "url" : "pix/i/stats.gif" }, + { "url" : "pix/i/questions.gif" }, + { "url" : "pix/i/files.gif" }, + { "url" : "pix/i/user.gif" }, + { "url" : "pix/i/user.gif" }, + { "url" : "pix/i/course.gif" }, + { "url" : "pix/i/course.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/forum/icon.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/resource/icon.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/resource/icon.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/resource/icon.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/resource/icon.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/resource/icon.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/resource/icon.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/resource/icon.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/resource/icon.gif" }, + { "url" : "pix/i/one.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/assignment/icon.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/assignment/icon.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/assignment/icon.gif" }, + { "url" : "pix/i/one.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/chat/icon.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/chat/icon.gif" }, + { "url" : "pix/i/one.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/choice/icon.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/choice/icon.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/choice/icon.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/choice/icon.gif" }, + { "url" : "pix/i/one.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/forum/icon.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "pix/i/one.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/glossary/icon.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/glossary/icon.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/glossary/icon.gif" }, + { "url" : "pix/i/one.gif" }, + { "url" : "pix/i/one.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/lesson/icon.gif" }, + { "url" : "pix/i/one.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/quiz/icon.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/quiz/icon.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/quiz/icon.gif" }, + { "url" : "pix/i/one.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/resource/icon.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/resource/icon.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/resource/icon.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/resource/icon.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "pix/f/web.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "pix/f/html.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "pix/f/web.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "pix/f/folder.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/resource/icon.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "pix/f/image.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "pix/f/audio.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "pix/f/flash.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "pix/f/avi.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "pix/f/powerpoint.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "pix/f/pdf.gif" }, + { "url" : "pix/i/one.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/scorm/icon.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/scorm/icon.gif" }, + { "url" : "pix/i/one.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/survey/icon.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/survey/icon.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/survey/icon.gif" }, + { "url" : "pix/i/one.gif" }, + { "url" : "pix/spacer.gif" }, + { "url" : "mod/wiki/icon.gif" }, + { "url" : "pix/i/one.gif" }, + { "url" : "mod/chat/icon.gif" }, + { "url" : "pix/docs.gif" }, + { "url" : "theme/standard/styles.php" }, + { "url" : "theme/standardwhite/styles.php" }, + { "url" : "theme/standardwhite/favicon.ico" }, + { "url" : "user/view.php?id=2&course=3" }, + { "url" : "" }, + { "url" : "help.php?module=moodle&file=switchrole.html&forcelang=" }, + { "url" : "mod/assignment/index.php?id=3" }, + { "url" : "mod/chat/index.php?id=3" }, + { "url" : "mod/choice/index.php?id=3" }, + { "url" : "mod/forum/index.php?id=3" }, + { "url" : "mod/glossary/index.php?id=3" }, + { "url" : "mod/lesson/index.php?id=3" }, + { "url" : "mod/quiz/index.php?id=3" }, + { "url" : "mod/resource/index.php?id=3" }, + { "url" : "mod/scorm/index.php?id=3" }, + { "url" : "mod/survey/index.php?id=3" }, + { "url" : "mod/wiki/index.php?id=3" }, + { "url" : "mod/forum/search.php?id=3" }, + { "url" : "help.php?module=moodle&file=search.html&forcelang=" }, + { "url" : "question/edit.php?courseid=3" }, + { "url" : "user/view.php?id=2&course=3" }, + { "url" : "course/view.php?id=3" }, + { "url" : "course/index.php" }, + { "url" : "mod/forum/view.php?id=2" }, + { "url" : "mod/resource/view.php?id=3" }, + { "url" : "mod/resource/view.php?id=4" }, + { "url" : "mod/resource/view.php?id=5" }, + { "url" : "mod/resource/view.php?id=6" }, + { "url" : "mod/resource/view.php?id=8" }, + { "url" : "mod/resource/view.php?id=9" }, + { "url" : "mod/resource/view.php?id=10" }, + { "url" : "mod/resource/view.php?id=11" }, + { "url" : "mod/assignment/view.php?id=13" }, + { "url" : "mod/assignment/view.php?id=14" }, + { "url" : "mod/assignment/view.php?id=15" }, + { "url" : "mod/chat/view.php?id=17" }, + { "url" : "mod/chat/view.php?id=18" }, + { "url" : "mod/choice/view.php?id=20" }, + { "url" : "mod/choice/view.php?id=21" }, + { "url" : "mod/choice/view.php?id=22" }, + { "url" : "mod/choice/view.php?id=23" }, + { "url" : "mod/forum/view.php?id=25" }, + { "url" : "mod/forum/view.php?id=26" }, + { "url" : "mod/forum/view.php?id=27" }, + { "url" : "mod/glossary/view.php?id=29" }, + { "url" : "mod/glossary/view.php?id=30" }, + { "url" : "mod/glossary/view.php?id=31" }, + { "url" : "mod/lesson/view.php?id=37" }, + { "url" : "mod/lesson/view.php?id=37&pageid=1" }, + { "url" : "mod/lesson/view.php?id=37&pageid=2" }, + { "url" : "mod/lesson/view.php?id=37&pageid=3" }, + { "url" : "mod/lesson/view.php?id=37&pageid=4" }, + { "url" : "mod/lesson/view.php?id=37&pageid=5" }, + { "url" : "mod/lesson/view.php?id=37&pageid=6" }, + { "url" : "mod/lesson/view.php?id=37&pageid=7" }, + { "url" : "mod/lesson/view.php?id=37&pageid=8" }, + { "url" : "mod/lesson/view.php?id=37&pageid=9" }, + { "url" : "mod/lesson/view.php?id=37&pageid=10" }, + { "url" : "mod/lesson/view.php?id=37&pageid=11" }, + { "url" : "mod/lesson/view.php?id=37&pageid=13" }, + { "url" : "mod/lesson/view.php?id=37&pageid=14" }, + { "url" : "mod/lesson/view.php?id=37&pageid=15" }, + { "url" : "mod/lesson/view.php?id=37&pageid=16" }, + { "url" : "mod/lesson/view.php?id=37&pageid=17" }, + { "url" : "mod/lesson/view.php?id=37&pageid=18" }, + { "url" : "mod/lesson/view.php?id=37&pageid=19" }, + { "url" : "mod/lesson/view.php?id=37&pageid=20" }, + { "url" : "mod/lesson/view.php?id=37&pageid=22" }, + { "url" : "mod/lesson/view.php?id=37&pageid=23" }, + { "url" : "mod/lesson/view.php?id=37&pageid=24" }, + { "url" : "mod/lesson/view.php?id=37&pageid=26" }, + { "url" : "mod/lesson/view.php?id=37&pageid=28" }, + { "url" : "mod/lesson/view.php?id=37&pageid=29" }, + { "url" : "mod/quiz/view.php?id=39" }, + { "url" : "mod/quiz/view.php?id=40" }, + { "url" : "mod/quiz/view.php?id=41" }, + { "url" : "mod/resource/view.php?id=43" }, + { "url" : "mod/resource/view.php?id=44" }, + { "url" : "mod/resource/view.php?id=45" }, + { "url" : "mod/resource/view.php?id=46" }, + { "url" : "mod/resource/view.php?id=47" }, + { "url" : "mod/resource/view.php?id=48" }, + { "url" : "mod/resource/view.php?id=49" }, + { "url" : "mod/resource/view.php?id=50" }, + { "url" : "mod/resource/view.php?id=51" }, + { "url" : "mod/resource/view.php?id=53" }, + { "url" : "mod/resource/view.php?id=54" }, + { "url" : "mod/resource/view.php?id=55" }, + { "url" : "mod/resource/view.php?id=56" }, + { "url" : "mod/resource/view.php?id=57" }, + { "url" : "mod/resource/view.php?id=58" }, + { "url" : "mod/scorm/view.php?id=60" }, + { "url" : "mod/scorm/view.php?id=61" }, + { "url" : "mod/survey/view.php?id=63" }, + { "url" : "mod/survey/view.php?id=64" }, + { "url" : "mod/survey/view.php?id=65" }, + { "url" : "mod/wiki/view.php?id=67" }, + { "url" : "mod/forum/discuss.php?d=4" }, + { "url" : "mod/forum/view.php?f=2" }, + { "url" : "mod/chat/view.php?id=17" }, + { "url" : "calendar/view.php?view=day&course=3&cal_d=28&cal_m=7&cal_y=2008" }, + { "url" : "calendar/view.php?view=upcoming&course=3" }, + { "url" : "user/view.php?id=2&course=3" }, + { "url" : "" }, + { "url" : "moodledata/3/Media_examples/Washington.mov" }, + { "url" : "moodledata/3/Media_examples/mitsy.wmv" }, + { "url" : "moodledata/3/Media_examples/Moodle_Logos/moodle-logo.jpg" }, + { "url" : "moodledata/3/Media_examples/Moodle_Logos/moodle-logo-trans.png" }, + { "url" : "moodledata/3/Media_examples/Moodle_Logos/moodle-logo-med.gif" }, + { "url" : "moodledata/3/Media_examples/Moodle_Logos/moodle-logo-small.gif" }, + { "url" : "moodledata/3/Media_examples/bonjour.mp3" }, + { "url" : "moodledata/3/Media_examples/muffins_from_space.swf" }, + { "url" : "moodledata/3/Media_examples/world-tour.mp3" }, + { "url" : "moodledata/3/Media_examples/testfile.html" }, + { "url" : "moodledata/3/Media_examples/Test.ppt" }, + { "url" : "moodledata/3/Media_examples/WQ3.pdf" } + ] +} diff --git a/offlinemoodle.js b/offlinemoodle.js new file mode 100755 index 0000000..411f385 --- /dev/null +++ b/offlinemoodle.js @@ -0,0 +1,139 @@ +var DB_NAME = "mdl_offline_db"; + +function ajaxget(id, page) { + alert('ajaxget ' + id + ' ' + page); + var pagecontent = fetchpage(id, page); + document.write(pagecontent); + document.close(); +} + +function ajxget(url, formname) { + alert('ajxget: ' + url + ' ' + formname); + var frm = document.getElementById(formname); + var id = frm.id.value; + var pageid = frm.pageid.value; + var page = eval(pageid); + if (frm.jumpto) { + var jumpto = eval(frm.jumpto.value); + if (jumpto == -1) { + page = page + 1; + } else if (jumpto > 0) { + page = jumpto; + } + } + alert('ajxget: formname ' + formname + ' id ' + id + ' pageid ' + pageid + ' jumpto ' + jumpto); + // get nextpage + var nextpage = getnextpage(page); + if( !frm.answerid) { + ajaxget(id, nextpage); + } else { + ajxget2(formname, frm, id, pageid, page); + } +} + +function ajxget2(formname, frm, id, pageid, page) { + var answervalue = 0; + var answertxt = ""; + var responsetxt = ""; + var jumpto, nextpage; + for (var i=0;i 0) { + page = jumpto; + } + // get nextpage + var nextpage = getnextpage(page); + // now create a response and write to document + var content = "Your answer :

"; + content = content + answertxt; + content = content + "

" + content = content + responsetxt; + content = content + "

"; + content = content + "

Continue

"; + var pagecontent = fetchpage(id,pageid); + var txt = pagecontent; + var start = txt.indexOf('
',start); + var newtxt = txt.substr(0,start) + content + txt.substring(end+7,txt.length); + document.write(newtxt); + document.close(); +} + +function getnextpage(page) { + // get info from db + var nextpage = page; + var db = google.gears.factory.create('beta.database', '1.0'); + db.open(DB_NAME); + if(db) { + var nextpage = page; + rs = db.execute("select nextpageid from mdl_lesson_pages where id =" + page); + if (rs.isValidRow()) { + nextpage = rs.field(0); + } else { + alert('row ' + page + ' not found in mdl_lesson_pages'); + } + rs.close(); + if(nextpage > 0) { + rs = db.execute("select jumpto, answer, response from mdl_lesson_answers where pageid =" + nextpage); + if (rs.isValidRow()) { + if (!(rs.field(1).length > 0 || rs.field(2).length > 0)) { + var nextpageid = rs.field(0); + if(nextpageid == -1) { + nextpage = nextpage + 1; + } else { + nextpage = nextpageid; + } + } + alert('end of branch returning to: ' + nextpage); + } + rs.close(); + } + db.close(); + } else { + alert("db not found"); + } + return(nextpage); +} + +function fetchpage(id,page) { + if (page > 0) { + var target = "view.php?id=" + id + "&pageid=" + page + } else { + var target = "http://tony-desktop/moodle/course/view.php?id=" + id + "&pageid=1"; + } + var req = new XMLHttpRequest(); + req.open("GET", target, false); + req.send(null); + if(req.status == 200) { + return(req.responseText); + } +} diff --git a/offlinemoodle.user.js b/offlinemoodle.user.js new file mode 100644 index 0000000..8822bfe --- /dev/null +++ b/offlinemoodle.user.js @@ -0,0 +1,176 @@ +// ==UserScript== +// @name offlinemoodle +// @namespace http://tony-desktop/moodle +// @description test greasemonkey script +// @include http://tony-desktop/moodle/* +// ==/UserScript== +var allLinks, thisLink, count, temp, n, insert; + +//document.body.setAttribute("onload", "init()"); + +function xfetch(xpath) { + var rslt = document.evaluate( + xpath, + document, + null, + XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, + null); + return rslt +} + +// case 1 image resource +allLinks = document.evaluate( + '//div/img', + document, + null, + XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, + null); +//alert(allLinks.snapshotLength) +count = 0 +for (var i = 0; i < allLinks.snapshotLength; i++) { + thisLink = allLinks.snapshotItem(i); + // do something with thisLink + var alt = thisLink.getAttribute('alt') + var altstr = 'the resource was not found' + thisLink.alt = altstr + var t = thisLink.getAttribute('src') + count = count + 1 + var teststr = 'moodle/file.php' + n = t.indexOf(teststr) + if (n > -1) { + temp = teststr + ' found at ' + n + ' ' + t + alert('case 1 ' + temp) + var newstr = 'http://tony-desktop/moodle/moodledata' + t.substr(n+15) + alert(newstr) + thisLink.src = newstr + } +} + +// case 2 office document +allLinks = document.evaluate( + '//frameset/frame', + document, + null, + XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, + null); +//alert(allLinks.snapshotLength) +count = 0 +for (var i = 0; i < allLinks.snapshotLength; i++) { + thisLink = allLinks.snapshotItem(i); + // do something with thisLink + var t = thisLink.getAttribute('src') + count = count + 1 + var teststr = 'moodle/file.php' + n = t.indexOf(teststr) + if (n > -1) { + temp = teststr + ' found at ' + n + ' ' + t + alert('case 2 ' + temp) + var newstr = 'http://tony-desktop/moodle/moodledata' + t.substr(n+15) + alert(newstr) + thisLink.src = newstr + } +} + +// case 3 pdf document +allLinks = document.evaluate( + '//object', + document, + null, + XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, + null); +//alert(allLinks.snapshotLength) +count = 0 +for (var i = 0; i < allLinks.snapshotLength; i++) { + thisLink = allLinks.snapshotItem(i); + // do something with thisLink + var t = thisLink.getAttribute('data') + count = count + 1 + var teststr = 'moodle/file.php' + n = t.indexOf(teststr) + if (n > -1) { + temp = teststr + ' found at ' + n + ' ' + t + alert('case 3 ' + temp) + var newstr = 'http://tony-desktop/moodle/moodledata' + t.substr(n+15) + alert(newstr) + thisLink.href = newstr + } +} + +// case 4 pdf document +allLinks = document.evaluate( + '//a', + document, + null, + XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, + null); +//alert(allLinks.snapshotLength) +count = 0 +for (var i = 0; i < allLinks.snapshotLength; i++) { + thisLink = allLinks.snapshotItem(i); + // do something with thisLink + var t = thisLink.getAttribute('href') + if (t) { + count = count + 1 + var teststr = 'moodle/file.php' + n = t.indexOf(teststr) + if (n > -1) { + temp = teststr + ' found at ' + n + ' ' + t + // alert('case 4 ' + temp) + var newstr = 'http://tony-desktop/moodle/moodledata' + t.substr(n+15) + // alert(newstr) + thisLink.href = newstr + } + } +} + +// case 5 file in directory +allLinks = document.evaluate( + '//a', + document, + null, + XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, + null); +// alert('case 5 ' + allLinks.snapshotLength) +count = 0 +for (var i = 0; i < allLinks.snapshotLength; i++) { + thisLink = allLinks.snapshotItem(i); + // do something with thisLink + var t = thisLink.getAttribute('onclick') + if (t) { + count = count + 1 + var teststr1 = 'this.target' + n = t.indexOf(teststr1) + if (n > -1) { + var teststr = 'file.php' + n = t.indexOf(teststr) + if (n > -1) { + temp = teststr + ' found at ' + n + var newstr = t.substr(0, n-13) + ' popup("http://tony-desktop/moodle/moodledata' + t.substr(n+8) + '"' + alert('case 5:' + temp + '\n' + t + '\n' + newstr + '\n' ) + thisLink = newstr + } + } + } +} + +// case 6 form +var allforms = xfetch("//form"); +for (var i = 0; i < allforms.snapshotLength; i++) { + // do something with thisLink + var form = allforms.snapshotItem(i); + if (form.id == 'pageform' || form.id.substr(0,10) == 'answerform') { + var url = form.getAttribute('action'); + url = url.replace('lesson.php', 'view.php'); + form.setAttribute('action', ""); + form.setAttribute('name', form.id); + var clickstr = "ajxget('" + url + "','" + form.id + "')"; + } + //alert(clickstr); + // Loop through all the form fields + for (var j = 0; j < form.elements.length; j++) { + if (form.elements[j].type == 'submit') { + form.elements[j].setAttribute("onclick",clickstr); + } + } + //alert(form.parentNode.innerHTML); +} diff --git a/readme b/readme new file mode 100644 index 0000000..b6e8d4b --- /dev/null +++ b/readme @@ -0,0 +1,91 @@ +These files represent a snapshot of the offline moodle project on Sept 2, 2008. This is a project in development and what +is described here is the development test environment. Any connection between this snapshot and a working capability may prove +to be embarassingly coincidental. + +Summary: + +This project is based on (Google) Gears to enable a student to work on Moodle course material while not connected to the server. +Gears uses a manifest file (manifest.json) to cache course pages and resources on the local computer (XO). This manifest file is +currently created by a python program which scans the course and subsidiary pages to identify the needed resources. This list must be +supplemented by some manual entries (based on trial and error). Gears then provides a localserver which intercepts requests for pages and resources +and responds using the cached data. If the requested data is not found, an error is shown. [for example, if view.php?id=37&page=21 is requested, a page with exactly that url must be in the local store or there is an error]. In addition, a set of javascript scripts (fetch_db.js, cache_db.js, and +(for debugging) display_db.js are used to cache information from the moodle database needed for navigation in the lesson module. The +web page go_offline.html provides buttons: capture (cause Gears to copy the files listed in manifest.json to a local database (the managed +store)), erase (to remove the data stored locally when reconnecting to the server - currently no attempt is made to update Moodle based on +the offline activity), fetch (invokes a php script (offlinemoodle.php) which gets information from the Moodle MySQL database on the server and stores it in +the Gears local SQLLite database), cache (finishes the job started by fetch, and display which shows the data stored in the local database +as needed for debugging. Fetch stores the data in a 'hidden' html div on the go_offline.html page and Cache reads that data and stores it in the Gears local db. Finally, a Greasemonkey javascript (offlinemoodle.user.js) is used to modify the Moodle pages so that calls to server-side php routines call instead client-side javascripts which perform the same functions. (Yes, you are right. This architecture owes a heavy debt to the work of Rube Goldberg!). + +Specific issues: + +1. The resource requests are based on files in the moodledata folder. For security reasons, this folder should not be in the moodle folder. In this case, an unauthorized request will fail because it is asking for data in a different domain. However, for exactly this reason, Gears also fails. Currently, the moodledata folder is in the moodle directory. What is needed is a server side php script (e.g. offlinemoodle.php) which can get the resources from the moodledata and supply them to Gears. However, Gears is based on the idea that it can intercept exactly the same 'url' as would be supplied online - because it targets the moodle domain. How to get over this is to be determined. + +2. The navigation of the lesson module depends on (1) going to the same page, (2) going to the next page, (3) going to a specified 'jumpto' page when the next page has no 'answer' and no 'response' in the Moodle mdl_lesson_answers table, (4) going to a previous page when the next page is an end of branch page (one that has an 'empty' entry in the mdl_lesson_pages table), and (5) going to the course page when the end of lesson (next page is page 0) is reached. In the snapshot some of this works. It's a bit like 'whack a mole'. Which work and which don't depends on the last one I tried to fix. Soon I hope to have all of it right. + +Install: + +1. You must have your own moodle install on 'your own' server. There are two required changes to moodle/lib/javascript.php which add script references to each page: + + + + + +The second and third lines were added after the first line (which is line 28 of the original). The second line provides a javascript script which processes XMLHTTPRequests (aka XHR or Ajax requests). This substitutes client-side javascript processing for (unavailable) server-side php processing. +The third line provides a javascript file to initialize Gears (needed on each page because I haven't found a way for javascript to remember how to refer to gears from one page to the next). + +2. You must install: gears_init.js, go_offline.html, and go_offline.js files to the moodle folder. These files are provided by the Gears example code. + +3. You must create an offline folder in the Moodle lib folder and put the offlinemoodle.js file there. + +4. The offline_manifest.json also goes in the Moodle folder. The one in the snapshot defines the pages and resources to be cached from the +Moodle Features Demo course. Naturally, when installing Moodle, you need to get the Moodle Features Demo course backup zip and install it on +your private Moodle. My 'private' server is named tony-desktop. Naturally, the relevant names in the json file need to be changed for your server name. + +5. The files fetch_db.js and fetch_db.php also need to be in the Moodle folder. + +6. In Firefox, install the Gears and Greasemonkey plugins. Installing 'firebug' can certainly help also. +Test: + +7. In Firefox, under tools/Greasemonkey, install new user script. Give it the name 'offlinemoodle.user.js'. In the includes - show only the url of +your Moodle (in my case: http://tony-desktop/moodle/*). The final '/*' means that the greasemonkey script will be applied to every moodle page (and no other pages). Hit OK. This will open the file in gedit (or your Firefox designated editor). Replace everything with the complete contents of the 'offlinemoodle.user.js' file in this package (and save). To make further changes, go to the Firefox/Tools/Greasemonkey/Manage User Scripts, select 'offlinemoodle.user.js', and hit edit on the lower left. This will reopen the page in gedit (or designated editor). It is actually saved somewhere in the bowels of Firefox. + +This is the procedure for testing the 'Features Demo' course offline: + +1. Login to the course online. I log in as admin and change role to student. This is essential as Gears must cache the pages created for a logged-in user. + +2. Fetch the 'go_offline.html' page. + +3. Go to Firefox/tools/clear private data. Clear the web page cache to ensure what you are seeing is really 'offline' Moodle. + +4. Press the 'capture' button. It will take some minutes to complete the cache process. The current snapshot does not provide a 'progress' +indicator. If it fails, it may be because you were not logged in to the course first. + +5. Press the 'fetch' button. It displays an alert when done. + +6. Press the 'cache' button. It displays an alert when done (currently saying the last line was 29). + +7. Go to Firefox/File and click on 'Work Offline'. I am not sure what happens if you don't do that and just try to test with the LAN/wifi link disabled. +To be determined. + +8. Go to Firefox/Tools/Greasemonkey and make sure it is enabled. + +9. Go back to Firefox and reload the course (the results this time are from the Gears cache). Try the activities to see what works. + +10. When the situation is hopeless, go to Firefox/File and return to online mode. Go to the 'go_offline.html' page. Hit the 'erase' button. + +11. Before starting another test make sure that (1) you reload the go_offline.html page so that it says again 'Yeah ...' and (2) clear the private data. +Note: except for the offlinemoodle.user.js (Greasemonkey) script, the other files are downloaded by Gears. This means that to test changes to these files, you must go back on line and do the 'capture' again to get the modified file. If the problem is in the Greasemonkey script, it can be changed and the results will be seen when the relevant (offline) web page is reloaded. + +Contents: + +offlinemoodle.js +offlinemoodle.user.js +offlinemoodle.php +fetch_db.js +fetch_db.php +offlinemanifest.json +buildmanifest.py +readme +go_offline.html +(gears_init.js and go_offline.js should be obtained from the Gears site). + -- 2.11.4.GIT
{$field->name}
$cell