Home Reference Source Repository

js/tools/resources/factories/ResourcesService.js

'use strict';
var ResourcesService = ($q, DataService, ModuleService, SyncAPIService, LoggerService) => {

	// A reference to a logger
	var LOG = LoggerService('ResourcesService');

	class ResourcesServiceImpl {
		_getData (moduleId){
			return ModuleService.getMergedToolData(moduleId, 'resources');
		}

		getItem(moduleId, id){
			return this._getData(moduleId).then(function(data){
				return data[id];
			});
		}

		/**
		* Get the parent directory object from the parent id
		*/
		getParent(moduleId, id){
			var pid = id;
			/**
			* This is a bit of a hack to get resources to work between Moodle and Sakai
			* Is the module id that we are working with is not fully numeric, it is assumed
			* to be a sakai module, therefore we have to add the /group id for the resource
			*
			* Else, if the number is fully numeric, it is a moodle module.
			*/
			if(pid == null){
				// 1 Check
				if (isNaN(moduleId)) {
					pid = '/group/' + moduleId + '/';
					return this.getItem(moduleId, pid);
				}
				// 2 Do moodle fancy stuff, and set pid to the resource you want
				return this._getData(moduleId).then(function (data) {
					for (var idx in data) {
						if (data.hasOwnProperty(idx)) {
							var item = data[idx];
							if (item.treeId.lastIndexOf(item.treeParentId, 0) === 0) {
								return item;
							}
						}
					}
					return null;
				});
			}
			return this.getItem(moduleId, pid);
		}


		/**
		* Gets all the children recursively that still needs to be downloaded
		*/
		getUndownloadedChildren(moduleId, parentId){
			var self = this;
			/*
			* Returns a promise to get the parent
			*/
			function getParentPromise(){
				return self.getParent(moduleId, parentId);
			}

			/*
			* A promise that will return a list of resources that must still be downloaded
			*/
			function findResourcesPromise(parentObject){
				var deferred = $q.defer();

				self._getData(moduleId).then(function(data){
					var directoryStack = [parentObject]; // List of directories still to be scanned
					var currentDirectory = null;
					var downloadObject = {
						'size' : 0,				// Total size of what we need to download
						'downloaded' : 0,		// Amount of data that we have downloaded
						'progress' : 0,			// Percentage of completion
						'downloadedFiles' : 0,	// Number of files that has been downloaded
						'resources' : []		// Resources we need to download
					};

					// Loop until there is nothing more
					while(directoryStack.length > 0){

						// Find the next directory in the stack
						currentDirectory = directoryStack.splice(0, 1)[0];
						LOG.debug('Currently looking at directory : ' + JSON.stringify(currentDirectory));

						// Loop through all of the data to find children
						for(var idx in data){
							var item = data[idx];

							// If this is a child from the parent
							if(item.treeParentId == currentDirectory.treeId){
								if(item.directory == true){
									LOG.debug('Adding directory to stack: ' + JSON.stringify(item));
									directoryStack.push(item);

								// Ignore urls and citations, we won't be downloading them
								}
								else if(item.mime_type == 'text/url' || item.resourceType == 'org.sakaiproject.citation.impl.CitationList'){
									delete data[item.id]; // remove this item from the big list

								// Add files that are not downloaded, ignoring urls
								}
								else if(!item.isDownloaded){
									LOG.debug('Adding file to list: ' + JSON.stringify(item));
									downloadObject.size += item.size;
									downloadObject.resources.push(item);
									delete data[item.id]; // remove this item from the big list
								}
							}
						}
					}
					deferred.resolve(downloadObject);
				});
				return deferred.promise;
			}

			return getParentPromise()
			.then(findResourcesPromise);
		}


		getDirectChildren(moduleId, parentId){
			var self = this;

			/*
			* Returns a promise to get the parent
			*/
			function getParentPromise(){
				return self.getParent(moduleId, parentId);
			}

			/*
			* Returns a promise to get the children
			*/
			function getChildrenPromise(parent){
				return self._getData(moduleId).then(function(data){
					var pId = parent == null ? null : parent.treeId;

					/* remove the trailing slash if there is one
					if(pId.lastIndexOf('/')+1 === pId.length){
					pId = pId.substring(0,pId.length-1);
				}*/

					var items = [];
					for(var key in data){
						var item = data[key];
						if(item.treeParentId === pId){
							items.push(item);
						}
					}
					return items;
				});
			}

			return getParentPromise()
			.then(getChildrenPromise);
		}


		/**
		* Returns a promise to physically delete and update the resources tool content for the
		* specified resource ID
		*/
		deleteResource(moduleId, resourceId){
			var self = this;		// A reference to ourself
			var resource = null; 	// The resource we are working with

			/**
			* Returns a promise to get the resource for an ID
			*/
			function getResourcePromise(){
				return self.getItem(moduleId, resourceId).then(function(theResource){
					resource = theResource;
					if(resource.isDownloaded !== undefined){
						delete resource.isDownloaded;
					}
					return theResource;
				});
			}

			/**
			* Returns a promise to physically delete the file from the filesystem
			*/
			function getDeleteFilePromise(resourceToDelete){
				var deletePath = resourceToDelete.treeId.replace(/[|&:;$%@'<>()+,]/g, '_');
				return DataService.deleteToolFile(moduleId, 'resources', deletePath);
			}

			/**
			* Returns a promise to persist the changes made to the resource to the filesystem
			*/
			function getSaveContentPromise(){
				var mergeData = {};
				mergeData[resource.id] = resource;
				return ModuleService.mergeToToolData(moduleId, 'resources', mergeData, false);
			}


			// Start the chain of actions
			return getResourcePromise()
			.then(getDeleteFilePromise)
			.then(getSaveContentPromise);
		}


		/**
		* Downloads a resource to the tool's data.
		* This function will also update the tool data to indicate that the resource
		* has been downloaded.
		*/
		downloadResource(moduleId, resource){
			var resourceAttachment;
			/*
			* Returns a promise to create an array of attachments to download.
			* This array will only contain one attachment for this resource
			*/
			function getAttachmentPromise(){
				var filename = resource.name;
				return DataService.createNewFile(filename).then((fileEntry) => {
					resourceAttachment = {
						'downloadKey' : resource.downloadKey,
						'downloadPath' : fileEntry.toInternalURL()
					};
				});
			}

			// Returns a promise to download the file
			function getDownloadAttachmentsPromise(){
				return SyncAPIService.getFileFromServer(resourceAttachment.downloadKey, resourceAttachment.downloadPath);
			}

			// Returns a promise to update the tool data that the resource is downloaded
			function getUpdateToolDataPromise(){
				var mergeData = {};
				mergeData[resource.id] = {
					'isDownloaded' : true,
					'downloadPath' : resourceAttachment.downloadPath
				};
				return ModuleService.mergeToToolData(moduleId, 'resources', mergeData, false).then(function(){
					return mergeData[resource.id];
				});
			}

			return getAttachmentPromise()
			.then(getDownloadAttachmentsPromise)
			.then(getUpdateToolDataPromise);
		}

	}
	return new ResourcesServiceImpl();
};
ResourcesService.$inject = ['$q', 'DataService', 'ModuleService', 'SyncAPIService', 'LoggerService'];
export default ResourcesService;