ScormPackage.groovy
/**
*
*/
package edu.odu.cs.cowem;
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.file.FileTree
import org.gradle.api.tasks.Copy
import org.gradle.api.tasks.Exec
import org.gradle.api.tasks.Sync
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream;
import java.io.StringReader
import java.io.StringWriter;
import java.lang.reflect.Array
import java.net.MalformedURLException
import java.nio.file.Files
import java.nio.file.FileSystem
import java.nio.file.FileSystems
import java.nio.file.Path
import java.nio.file.Paths
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipOutputStream
import edu.odu.cs.cowem.documents.MarkdownDocument
import edu.odu.cs.cowem.documents.Utils;
/**
* Prepares a SCORM 1.2 package suitable for importation into Blackbaord
* and many other Learning Management Systems.
*
* @author zeil
*
*/
class ScormPackage {
private static Logger logger =
LoggerFactory.getLogger(ScormPackage.class);
Project project;
Course course;
File buildDir;
File tempAreaAbs;
File webcontentAbs;
String webcontentRel;
/**
* Initialize the package builder
* @param theProject the website project
* @param theCourse the course website descriptor
* @param theBuildDirectory the project build directory
*/
ScormPackage (Project theProject, Course theCourse,
File theBuildDirectory)
{
project = theProject
course = theCourse
buildDir = theBuildDirectory
}
/**
* Generate the package suitable for import
* @param destination where to store the resulting package
* @param thin if true, package will contain a modules outline and
* calendar entries but the outline will link to an external website
* for actual content. If false, the full content is included in the
* package.
*/
void generate (File destination)
{
destination.getParentFile().mkdirs()
tempAreaAbs = new File(new File(buildDir, "temp"), "scorm")
if (tempAreaAbs.exists()) {
tempAreaAbs.deleteDir() // Groovy extension to java.io.File
}
tempAreaAbs.mkdirs()
def manifestFiles = copyFiles ()
buildManifest (manifestFiles)
zipItAllUp (destination)
}
FileTree copyFiles ()
{
webcontentAbs = tempAreaAbs
webcontentAbs.mkdirs()
def websiteFiles = project.fileTree(
dir: 'build/website/', include: '**/*')
project.copy {
from websiteFiles
into webcontentAbs
}
project.delete (new File(webcontentAbs, 'index.html'))
project.copy {
into webcontentAbs
from ({ project.zipTree(project.buildscript.configurations.classpath.find {
it.name.startsWith("cowem-plugin") }
) }) {
include 'edu/odu/cs/cowem/core/scorm/**'
}
}
return websiteFiles
}
void buildManifest (FileTree websiteFiles)
{
def manifest = new BufferedWriter(
new FileWriter(new File(tempAreaAbs, 'imsmanifest.xml'))
)
def manifestPrefix = [
'<?xml version="1.0" standalone="no" ?>',
'<manifest identifier="com.scorm.golfsamples.contentpackaging.singlesco.12" version="1"',
' xmlns="http://www.imsproject.org/xsd/imscp_rootv1p1p2"',
' xmlns:adlcp="http://www.adlnet.org/xsd/adlcp_rootv1p2"',
' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"',
' xsi:schemaLocation="http://www.imsproject.org/xsd/imscp_rootv1p1p2 imscp_rootv1p1p2.xsd',
' http://www.imsglobal.org/xsd/imsmd_rootv1p2p1 imsmd_rootv1p2p1.xsd',
' http://www.adlnet.org/xsd/adlcp_rootv1p2 adlcp_rootv1p2.xsd">',
' <metadata>',
' <schema>ADL SCORM</schema>',
' <schemaversion>1.2</schemaversion>',
' </metadata>',
' <organizations default="default_org">',
' <organization identifier="default_org">']
for(String line: manifestPrefix) {
manifest.println (line);
}
manifest.println ('<title>' + course.courseName + '</title>')
manifest.println ('<item identifier="item_1" identifierref="resource_1">')
manifest.println (' <title>' + course.courseName + '</title>')
manifest.println('</item>')
manifest.println('</organization>')
manifest.println('</organizations>')
manifest.println('<resources>')
manifest.println(' <resource identifier="resource_1" type="webcontent" adlcp:scormtype="sco" href="Directory/outline/index.html">')
def websiteBase = project.file('build/website')
for (File f: websiteFiles.getFiles()) {
if (!f.isDirectory()) {
Path p = f.toPath()
Path relPath = websiteBase.toPath().relativize(p)
String relPathStr = relPath.toString().replace('\\', '/')
if (!relPathStr.equals('index.html')) {
manifest.println('<file href="' + relPathStr + '"/>')
}
}
}
manifest.println("</resource>\n</resources>\n</manifest>\n")
manifest.close()
}
void zipItAllUp (File destination)
{
File bbDir = project.file("build/temp/scorm")
Path bbBase = bbDir.toPath();
ZipOutputStream zout = new ZipOutputStream(new FileOutputStream(destination));
zout.close();
Path zipfile = destination.toPath();
Queue<File> q = new LinkedList<File>();
q.push(bbDir);
FileSystem zipfs = FileSystems.newFileSystem(zipfile, null);
if (zipfs == null) {
logger.error ("Could not create zip file system at " + zipfile)
}
while (!q.isEmpty()) {
File dir = q.remove()
for (File child : dir.listFiles()) {
Path relChild = bbBase.relativize(child.toPath());
if (child.isDirectory()) {
q.add(child);
Path directory = zipfs.getPath("/", relChild.toString())
Files.createDirectories(directory)
} else {
Path childLoc = zipfs.getPath("/", relChild.toString())
Files.copy(child.toPath(), childLoc)
}
}
}
zipfs.close()
}
}