Commit d6baa866 authored by Frank Bergmann's avatar Frank Bergmann

- Now downloading and uploading

parent 6c6c538a
......@@ -19,20 +19,18 @@ const {ipcRenderer} = require('electron')
Ext.Loader.setPath('POSync', '.');
Ext.Loader.setConfig({disableCaching: false});
Ext.require([
Ext.require([
'POSync.model.File',
'POSync.model.Project',
'POSync.model.LocalFile',
'POSync.model.Operation',
'POSync.store.Files',
'POSync.store.Projects',
'POSync.store.LocalFiles',
'POSync.store.Operations',
'POSync.view.LoginPanel',
'POSync.view.ProjectsPanel',
'POSync.controller.Gui',
'POSync.controller.Connection'
]);
......@@ -111,8 +109,8 @@ Ext.define('POSync.view.Viewport', {
defaults: set_defaults(3)
}),
items: [
Ext.create('POSync.view.LoginPanel'),
{
Ext.create('POSync.view.LoginPanel'),
{
title: 'Projects',
items: {
xtype: 'grid',
......@@ -120,30 +118,21 @@ Ext.define('POSync.view.Viewport', {
store: 'Projects',
/* FIXME: emptyText initially not shown */
emptyText: 'No projects available',
columns: [{
text: 'Id',
dataIndex: 'project_id',
align: 'right',
flex: 1
}, {
text: 'Number',
dataIndex: 'project_nr',
flex: 2
}, {
text: 'Name',
dataIndex: 'project_name',
flex: 7
}]
columns: [
{text: 'Id', dataIndex: 'project_id', align: 'right', flex: 1},
{text: 'Number', dataIndex: 'project_nr', flex: 2},
{text: 'Name', dataIndex: 'project_name', flex: 7}
]
}
},{
title: 'Files',
items: {
xtype: 'treepanel',
itemId: 'files',
store: 'Files',
rootVisible: false,
useArrows: true,
/* FIXME: emptyText initially not shown */
store: Ext.create('POSync.store.Files', {
storeId: 'remoteFiles'
}),
rootVisible: false,
emptyText: 'No files available',
tbar: {
xtype: 'combobox',
......@@ -156,136 +145,44 @@ Ext.define('POSync.view.Viewport', {
forceSelection: true,
emptyText: 'No project selected',
},
columns: [{
xtype: 'treecolumn',
text: 'Name',
dataIndex: 'name',
flex: 6
}, {
text: 'Title',
dataIndex: 'title',
flex: 6
}, {
text: 'Description',
dataIndex: 'description',
flex: 6
}, {
text: 'Length',
dataIndex: 'content_length',
align: 'right',
flex: 2
}, {
text: 'Publish Date',
dataIndex: 'publish_date',
flex: 6
}, {
text: 'Id',
dataIndex: 'id',
align: 'right',
flex: 1,
hidden: true
}, {
text: 'Version',
dataIndex: 'version_id',
align: 'right',
flex: 1,
hidden: true
}, {
text: 'Sha1',
dataIndex: 'sha1',
flex: 6,
hidden: false
}, {
text: 'Level',
dataIndex: 'level',
align: 'right',
flex: 1,
hidden: true
}, {
text: 'Expanded',
dataIndex: 'expanded',
flex: 1,
hidden: true
}, {
text: 'Leaf',
dataIndex: 'leaf',
flex: 1,
hidden: true
}, {
text: 'Type',
dataIndex: 'type',
flex: 3,
hidden: false
}, {
text: 'IconCls',
dataIndex: 'iconCls',
flex: 4,
hidden: true
}, {
text: 'Mime Type',
dataIndex: 'mime_type',
flex: 3,
hidden: true
}]
columns: [
{text: 'Name', xtype: 'treecolumn', dataIndex: 'name', flex: 6},
{text: 'Type', dataIndex: 'type', flex: 3, hidden: false},
{text: 'Mime Type',dataIndex: 'mime_type',flex: 3, hidden: false},
{text: 'Length', dataIndex: 'content_length', align: 'right', flex: 2, hidden: false},
{text: 'Date', dataIndex: 'modification_date', flex: 6, hidden: false},
{text: 'Path', dataIndex: 'path', flex: 6, hidden: false},
{text: 'Title', dataIndex: 'title', flex: 6, hidden: true},
{text: 'Description', dataIndex: 'description', flex: 6, hidden: true},
{text: 'Length', dataIndex: 'content_length', align: 'right', flex: 2, hidden: true},
{text: 'Id', dataIndex: 'id', align: 'right', flex: 1, hidden: true},
{text: 'Version', dataIndex: 'version_id', align: 'right', flex: 1, hidden: true},
{text: 'Sha1', dataIndex: 'sha1', flex: 6, hidden: true}
]
}
},{
title: 'Local directory',
items: {
xtype: 'treepanel',
itemId: 'localdir',
store: 'LocalFiles',
rootVisible: false,
useArrows: true,
/* FIXME: emptyText initially not shown */
store: Ext.create('POSync.store.Files', {
storeId: 'localFiles',
proxy: Ext.create('Ext.data.proxy.Memory'),
root: {expanded: true,children: []}
}),
rootVisible: false,
emptyText: 'No files available',
columns: [{
xtype: 'treecolumn',
text: 'Name',
dataIndex: 'name',
flex: 6
}, {
text: 'Type',
dataIndex: 'type',
flex: 3,
hidden: false
}, {
text: 'Size',
dataIndex: 'size',
align: 'right',
flex: 2,
hidden: false
}, {
text: 'Date',
dataIndex: 'date',
flex: 6,
hidden: false
}, {
text: 'Sha1',
dataIndex: 'sha1',
flex: 6,
hidden: false
}, {
text: 'Depth',
dataIndex: 'depth',
align: 'right',
flex: 1,
hidden: true
}, {
text: 'Expandable',
dataIndex: 'expandable',
flex: 1,
hidden: true
}, {
text: 'Expanded',
dataIndex: 'expanded',
flex: 1,
hidden: true
}, {
text: 'Leaf',
dataIndex: 'leaf',
flex: 1,
hidden: true
}]
columns: [
{text: 'Name', xtype: 'treecolumn', dataIndex: 'name', flex: 6},
{text: 'Type', dataIndex: 'type', flex: 3, hidden: false},
{text: 'Mime Type',dataIndex: 'mime_type',flex: 3, hidden: false},
{text: 'Length', dataIndex: 'content_length', align: 'right', flex: 2, hidden: false},
{text: 'Date', dataIndex: 'modification_date', flex: 6, hidden: false},
{text: 'Path', dataIndex: 'path', flex: 6, hidden: false},
{text: 'Sha1', dataIndex: 'sha1', flex: 6, hidden: true},
{text: 'Expandable', dataIndex: 'expandable', flex: 1, hidden: true},
{text: 'Expanded', dataIndex: 'expanded', flex: 1, hidden: true}
]
}
},{
title: 'About',
......@@ -309,22 +206,10 @@ Ext.define('POSync.view.Viewport', {
].join('<br/>')
}]
},
buttons: [{
text: 'Login',
action: 'login'
}, {
text: 'Projects',
action: 'projects'
}, {
text: 'Files',
action: 'files'
}, {
text: 'Local directory',
action: 'localdir'
}, {
text: 'Sync',
action: 'sync'
}]
buttons: [
{text: 'Login', action: 'login'},
{text: 'Sync', action: 'sync'}
]
}
/* End of POSync.view.Viewport */
......@@ -341,8 +226,8 @@ var app = Ext.application({
id: 'POSync',
itemId: 'POSync',
autoCreateViewport: true,
models: ['Project', 'File', 'LocalFile', 'Operation'],
stores: ['Projects', 'Files', 'LocalFiles', 'Operations'],
models: ['Project', 'File', 'Operation'],
stores: ['Projects', 'Files', 'Operations'],
controllers: ['Gui', 'Connection'],
})
......
......@@ -29,8 +29,10 @@ Ext.define('POSync.controller.Connection', {
console.log ('Connection.onLaunch: controller onLaunch');
this.projectsStore = this.getProjects().getStore();
this.filesStore = this.getFiles().getStore();
this.localDirStore = this.getLocalDir().getStore();
this.filesStore = this.getFiles().getStore();
this.localDirStore = this.getLocalDir().getStore();
this.operationsStore = Ext.StoreManager.get('Operations');
var app = this.getApplication();
......@@ -83,6 +85,7 @@ Ext.define('POSync.controller.Connection', {
me.loadProjects();
}
});
},
/* Send a request to get the list of projects */
......@@ -141,6 +144,12 @@ Ext.define('POSync.controller.Connection', {
}
me.filesStore.isLoaded = projectId;
var root = me.filesStore.getRootNode();
root.cascadeBy(function(file) {
file.set('expandable', true);
//file.set('expanded', true);
});
// Execute callback
successCallback && successCallback.call(me, projectId);
}
......@@ -154,14 +163,10 @@ Ext.define('POSync.controller.Connection', {
*/
loadLocalDirectory: function (projectId) {
console.log('Connection.loadLocalDirectory: '+projectId);
// Skip if already loaded
// if (''+this.localDirStore.isLoaded == ''+projectId) return;
var configData = this.guiController.configData;
if (!this.projectsStore.isLoaded) { alert('Projects not yet loaded'); return; }
// Get the project path
var configData = this.guiController.configData;
var project = this.projectsStore.getById(''+projectId);
var path = configData.topdir+'/'+project.get('project_path');
......@@ -170,9 +175,9 @@ Ext.define('POSync.controller.Connection', {
if (!fs.existsSync(path)) { fs.mkdir(path); }
// Get the contents
const dirTree = require ('directory-tree');
const tree = dirTree (path);
this.fixLocalTree (tree);
var dirTree = require ('directory-tree');
var tree = dirTree(path,{attributes:['mode', 'mtime', 'mtimeMs']});
this.fixLocalTree(tree);
this.localDirStore.loadTree (tree);
this.localDirStore.isLoaded = projectId;
......@@ -189,12 +194,11 @@ Ext.define('POSync.controller.Connection', {
tree.leaf = false;
tree.mime_type = '';
} else {
const cp = require('child_process');
// const cp = require('child_process');
// tree.type = cp.execFileSync('/usr/bin/file', ['--brief', '--mime-type', tree.path]).toString();
// tree.sha1 = cp.execFileSync('/usr/bin/sha1sum', [tree.path]).toString().substr(0,40);
// tree.date = cp.execFileSync('/usr/bin/stat', ['-c', '%y', tree.path]).toString();
tree.type = 'unknown';
tree.sha1 = '<sha1>';
tree.date = '<date>';
......@@ -221,48 +225,114 @@ Ext.define('POSync.controller.Connection', {
var me = this;
console.log('Connection.sync');
// Load the list of local files needed for sync
this.loadLocalDirectory(projectId);
// Load the list of files and continue with syncCallback
this.loadFiles(projectId, me.syncCallback);
this.loadFiles(projectId, me.syncFilesLoadedCallback);
},
/**
* The list of files has arrived.
* Now we can start with the actual sync
*/
syncCallback: function(projectId) {
syncFilesLoadedCallback: function(projectId) {
var me = this;
console.log('Connection.syncCallback');
var configData = this.guiController.configData;
// Clean up the operations store
me.operationsStore.removeAll();
// ********************************************************************
// Go through the list of remote files and check that a local file exists.
var root = me.filesStore.getRootNode();
if (''+me.projectsStore.isLoaded != ''+projectId) this.loadLocalDirectory(projectId);
root.cascadeBy(function(file) {
// Exclude folders and the root
if ("folder" == file.get('type')) return;
if ("root" == file.get('id')) return;
if ("root" == file.get('id')) return; // Exclude folders and the root
var pathArray = file.getPathArray();
console.log('Connection.sync: path='+pathArray);
console.log(file);
var purePathArray = pathArray.slice(0);
var fileName = file.get('name');
pathArray.push(fileName);
console.log('Connection.sync remote: path='+pathArray);
var type = file.get('type');
switch (type) {
case 'file':
var localFileFound = me.localDirStore.searchUsingPathArray(pathArray);
if (!localFileFound) {
var operation = Ext.create('POSync.model.Operation', {
id: 'op_'+file.get('id'),
file_id: file.get('id'),
file_path: purePathArray.join('/'),
file_name: file.get('name'),
mime_type: file.get('mime_type'),
file_sha1: null,
op: 'download'
});
me.operationsStore.add(operation);
}
break;
case 'folder':
var localFileFound = me.localDirStore.searchUsingPathArray(purePathArray);
if (!localFileFound) {
// Create a new local folder
var fs = require('fs');
// Get the project path
var project = me.projectsStore.getById(''+projectId);
var projectPath = configData.topdir+'/'+project.get('project_path');
var path = projectPath+'/'+purePathArray.join('/');
if (!fs.existsSync(path)) { fs.mkdir(path); }
}
break;
default: alert('sync: invalid type for remote file: '+type);
}
});
var localFileFound = me.localDirStore.searchUsingPathArray(pathArray);
console.log(localFileFound);
// ********************************************************************
// Go through the list of local files and check that a remote file exists
var root = me.localDirStore.getRootNode();
root.cascadeBy(function(file) {
if ("root" == file.get('id')) return; // Exclude root
if (!localFileFound) {
var pathArray = file.getPathArray();
var purePathArray = pathArray.slice(0);
var fileName = file.get('name');
pathArray.push(fileName);
console.log('Connection.sync local: path='+pathArray);
var type = file.get('type');
switch (type) {
case 'file':
var remoteFileFound = me.filesStore.searchUsingPathArray(pathArray);
if (!remoteFileFound) {
var operation = Ext.create('POSync.model.Operation', {
id: 'op_'+file.get('id'),
file_id: file.get('id'),
file_path: pathArray.join('/'),
file_path: purePathArray.join('/'),
file_name: file.get('name'),
mime_type: file.get('mime_type'),
file_sha1: null,
op: 'create'
op: 'upload'
});
me.operationsStore.add(operation);
}
break;
case 'folder':
var remoteFileFound = me.filesStore.searchUsingPathArray(purePathArray);
if (!remoteFileFound) {
var operation = Ext.create('POSync.model.Operation', {
id: 'op_'+file.get('id'),
file_id: file.get('id'),
file_path: purePathArray.join('/'),
file_name: file.get('name'),
op: 'mkdir'
});
me.operationsStore.add(operation);
}
break;
default: alert('sync: invalid type for local file: '+type);
}
});
......@@ -270,7 +340,6 @@ Ext.define('POSync.controller.Connection', {
},
/**
* Takes a store with sync "Operations" and executes them.
* This performs the actual sync.
......@@ -285,8 +354,14 @@ Ext.define('POSync.controller.Connection', {
var operation = op.get('op');
switch (operation) {
case 'create':
me.syncCreate(projectId, op);
case 'download':
me.syncDownload(projectId, op);
break
case 'upload':
me.syncUpload(projectId, op);
break
case 'mkdir':
me.syncMkdir(projectId, op);
break
default:
alert('syncExecOperations: Internal error: unknown operation='+operation);
......@@ -296,16 +371,16 @@ Ext.define('POSync.controller.Connection', {
},
/**
* Download a single file.
* Download a remote file to the local file-system.
*
* Called from syncExecOperations, which in turn
* loops through the list of operations created during
* sync.
*/
syncCreate: function(projectId, op) {
syncDownload: function(projectId, op) {
var me = this;
console.log('Connection.syncCreate');
console.log(op);
console.log('Connection.syncDownload');
var configData = this.guiController.configData;
var fileId = op.get('file_id');
var filePath = op.get('file_path');
......@@ -329,7 +404,7 @@ Ext.define('POSync.controller.Connection', {
xhr.onload = function () {
// Asynchronously saving files after they have been downloaded
if (this.status === 200) {
console.log('Connection.syncCreate: successful download - saving');
console.log('Connection.syncDownload: successful download - saving');
// Decode the response encoded by server in base64
var responseBuffer = xhr.response;
......@@ -340,13 +415,94 @@ Ext.define('POSync.controller.Connection', {
if (!fs.existsSync(path)) { fs.mkdir(path); }
fs.writeFile(path+"/"+fileName, buf64, function() {});
console.log('Connection.syncCreate: written file='+filePath);
console.log('Connection.syncDownload: written file='+path+'/'+fileName);
} else {
console.log('Connection.syncCreate: download status='+this.status);
console.log('Connection.syncDownload: download status='+this.status);
}
};
xhr.send();
},
/**
* Upload a local file to the remote file-storage
*/
syncUpload: function(projectId, op) {
var me = this;
console.log('Connection.syncUpload');
var configData = this.guiController.configData;
var filePath = op.get('file_path');
var fileName = op.get('file_name');
var mimeType = op.get('mime_type');
// Get the project path
var project = this.projectsStore.getById(''+projectId);
var projectPath = configData.topdir+'/'+project.get('project_path');
var path = projectPath+'/'+filePath;
var mimeType = 'text/xml';
// make sure the path exists
var fs = require('fs');
var fileContent = fs.readFileSync(path+"/"+fileName);
// POST the file to the upload page
var xhr = new XMLHttpRequest();
var url = configData.url + '/intranet-rest-fs-openacs/upload';
var formData = new FormData();
formData.append('project_id', projectId);
formData.append('path', filePath);
// formData.append('upload_file', fileContent);
var blob = new Blob([fileContent], { type: mimeType});
formData.append('upload_file', blob, fileName);
// xhr.responseType = "arraybuffer";
// xhr.submittedData = fileContent;
xhr.open("POST", url, true);
// xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
// xhr.setRequestHeader('X-Requested-With','XMLHttpRequest');
xhr.onload = function() {
// Asynchronously saving files after they have been downloaded
alert(this.responseText);
if (this.status === 200) {
console.log('Connection.syncUpload: successful upload');
} else {
console.log('Connection.syncUpload: upload status='+this.status);
}
};
xhr.send(formData);
},
/**
* Create a new folder in the remote file-storage
*/
syncMkdir: function(projectId, op) {
var me = this;
console.log('Connection.syncMkdir');
// POST the file to the mkdir page
var configData = this.guiController.configData;
var filePath = op.get('file_path');
var xhr = new XMLHttpRequest();
var url = configData.url + '/intranet-rest-fs-openacs/mkdir';
var formData = new FormData();
formData.append('project_id', projectId);
formData.append('path', filePath);
xhr.open("POST", url, true);
xhr.onload = function() {
if (this.status === 200) {
console.log('Connection.syncUpload: successful upload');
} else {
alert(this.responseText);
console.log('Connection.syncUpload: upload status='+this.status+': '+this.responseText);
}
};
xhr.send(formData);
}
/* End of POSync.controller.Connection */
});
/*
* Data related to a file.
*
......@@ -10,24 +9,35 @@ Ext.define('POSync.model.File', {
extend: 'Ext.data.TreeModel',
idProperty: 'id',
fields: [
'id', // The system wide unique ID for this object (-> cr_items.item_id)
// FIXME: could better be file_id
'name', // The name and extension (without path) of the file or folder
'version_id', // The active version of the file (-> cr_revisions.revision_id) (empty string for folders)
'description', // Description of the file or folder
'title', // Title of the file or folder
'id', // The system wide unique ID for this object (=file_id)
'name', // The name (with extension) of the file or directory
'path', // The absolute path relative to the project folder, excluding name
'extension', // file extension
'type', // 'file' or 'directory'
'mime_type', // MIME type, empty for folders
'local_p', // 't' for a local file (client), 'f' for remote (server) file
'title', // Title of the file or folder
'description', // Description of the file or folder
'content_length', // File length in bytes (empty string for folders)
'creation_date', // Date of first creation
'publish_date',
'modification_date', // Date of last modification
'sha1', // sha-1 hash of the content (empty string for folders)
// OpenACS properites
'file_id', // ]po[ object_id of the file
'version_id', // The version of the file (cr_revisions.revision_id), empty for folders
// Sencha properties
'level',
'children', // FIXME: To be deleted
'iconCls', // Used by ExtJS for the icon, seems to work
// FIXME: should be generated automatically from 'type' (icon-TYPE)
'content_length', // File length in bytes (empty string for folders)
'sha1', // sha-1 Hash of the content (empty string for folders)
'expanded', // true or false (without quotes), default state for tree
'type', // 'folder' for folders and the mime-type for file
'leaf', // false for folders, true for files
// added by SALO
'level', // FIXME: let ExtJS calculate this
'mime_type', // FIXME: same as 'type' for files, and empty string for folders
'publish_date', // empty string for folders
'children' // undefined for files, a (possibly empty) array for folders
'expanded', // default state for tree, true or false (without quotes)
'expandable', // not sure
'leaf', // true if node has no children
],
/**
......@@ -38,14 +48,11 @@ Ext.define('POSync.model.File', {
// Skip the root and the 1st level folder, which refers to the project itself.
if ("root" == me.get('id')) return [];
if ("root" == me.get('parentId')) return [];
//if ("root" == me.get('parentId')) return [];
var fileType = me.get('type');
var parentId = me.get('parentId');
if (!parentId) return [me.get('name')];
var fileStore = Ext.StoreManager.get('Files');
var parent = fileStore.getById(parentId);
if (!parent) return [me.get('name')];
var parent = me.parentNode;
if (!parent) return [];
var parentPath = parent.getPathArray();
// For a file return just the path (=parentPath)
......
......@@ -12,6 +12,7 @@ Ext.define('POSync.store.Files', {
requires: 'POSync.model.File',
model: 'POSync.model.File',
nodeParam: 'object_id',
root: {
expanded: true,
children: []
......@@ -24,5 +25,46 @@ Ext.define('POSync.store.Files', {
type: 'json',
root: 'children'
}
},
/**
* Load the store with tree (from local directory)
*/
loadTree: function (tree) {
var proxy = this.getProxy();
proxy.data = tree;
this.load();
},
/**
* Check if a file with the given path exists
*/
searchUsingPathArray: function(pathArray) {
var me = this;
var root = me.getRootNode();
// root = root.childNodes[0]; // root is the project's root, so we need to start one below
// Iterate through the pathArray elements from
// the top to find the file in the local folder
var node = root;
for (var i = 0; i < pathArray.length; i++) {
var path = pathArray[i];
// Check all children
var child = null;
var children = node.childNodes;
if (!children) return null;
for (var j = 0; j < children.length; j++) {
var childName = children[j].get('name');
if (path === childName) {
child = children[j];
}
}
if (!child) return null;
node = child;
}
return child;
}
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment