pax_global_header 0000666 0000000 0000000 00000000064 13604101074 0014506 g ustar 00root root 0000000 0000000 52 comment=2b70a91653a880a2ef9f2c3abcbcc8c445444221
intranet-rest-fs-openacs-b5-0-3-patches/ 0000775 0000000 0000000 00000000000 13604101074 0020133 5 ustar 00root root 0000000 0000000 intranet-rest-fs-openacs-b5-0-3-patches/intranet-rest-fs-openacs.info 0000664 0000000 0000000 00000002441 13604101074 0025644 0 ustar 00root root 0000000 0000000
]project-open[ REST Filestorage OpenACS
]project-open[ REST Filestorage OpenACS
f
t
f
f
intranet-rest-fs-openacs
Frank Bergmann
REST Interface to OpenACS filestorage
]project-open[ Business Solutions, S.L.
Implements a REST interface to OpenACS filestorage
0
intranet-rest-fs-openacs-b5-0-3-patches/lib/ 0000775 0000000 0000000 00000000000 13604101074 0020701 5 ustar 00root root 0000000 0000000 intranet-rest-fs-openacs-b5-0-3-patches/lib/file-tree.adp 0000664 0000000 0000000 00000007243 13604101074 0023251 0 ustar 00root root 0000000 0000000
intranet-rest-fs-openacs-b5-0-3-patches/lib/file-tree.tcl 0000664 0000000 0000000 00000001046 13604101074 0023262 0 ustar 00root root 0000000 0000000 # /intranet-rest-fs-openacs/lib/file-tree.tcl
#
# Variables from page:
#
# object_id
# ---------------------------------------------------------
#
# ---------------------------------------------------------
set return_url [im_url_with_query]
set current_user_id [auth::require_login]
# Create a random ID for the gantt editor
set file_tree_rand [expr {round(rand() * 100000000.0)}]
set file_tree_id "file_tree_$file_tree_rand"
# Get or create the folder related to object_id
set folder_id [im_rest_fs_folder_for_object -object_id $object_id]
intranet-rest-fs-openacs-b5-0-3-patches/posync/ 0000775 0000000 0000000 00000000000 13604101074 0021446 5 ustar 00root root 0000000 0000000 intranet-rest-fs-openacs-b5-0-3-patches/posync/app.js 0000664 0000000 0000000 00000020575 13604101074 0022575 0 ustar 00root root 0000000 0000000 /*
* ]project-open[ FS Sync
* Copyright (c) 2018 ]project-open[
* Authors:
* - Gonzalo Pérez de Olaguer Córdoba
* - Frank Bergmann
*
* This code is licensed under the GNU GPL version 2.0 or later
*
* This is the application window script.
* It creates the application window.
*
* This script requires Sencha Ext/JS 4.2.1
*/
/* Load Electron inter process communication (IPC) */
const {ipcRenderer} = require('electron')
Ext.Loader.setPath('POSync', '.');
Ext.Loader.setConfig({disableCaching: false});
Ext.require([
'POSync.model.File',
'POSync.model.Project',
'POSync.model.Operation',
'POSync.store.Files',
'POSync.store.Projects',
'POSync.store.Operations',
'POSync.view.LoginPanel',
'POSync.view.ProjectsPanel',
'POSync.controller.Gui',
'POSync.controller.Connection'
]);
var node_version = process.versions.node;
var chrome_version = process.versions.chrome;
var electron_version = process.versions.electron;
var extjs_version = Ext.getVersion().version;
/*
* Debugging function that sets margin, border, padding and
* background color to help identify the layout of the views
*/
var bg_colors = ["#ff0000", "#00ff00", "#0000ff", "#ffff00", "#ff00ff", "#00ffff" ];
/************************************************************
*
* GUI
*
************************************************************/
/*
* A form field to select a local directory.
*/
Ext.define('Ext.ux.DirField', {
extend: 'Ext.form.field.Trigger',
alias: 'widget.dirfield',
/* Clicking the button opens a select directory dialog provided by electron */
onTriggerClick: function() {
const {dialog} = require('electron').remote;
var path = dialog.showOpenDialog({
title: 'Select the top directory to synchronize',
properties: ['openDirectory']
});
if (path)
this.setValue (path);
}
});
function set_defaults (level, opts) {
/* set to true(false) to enable(disable) coloured layouts */
if (false) {
if (!opts)
opts = {};
opts.style = {
margin: '2px',
padding: '2px',
border: '1px solid black',
background: bg_colors[level % 6],
};
}
return opts;
}
/* The application GUI */
Ext.define('POSync.view.Viewport', {
extend: 'Ext.container.Viewport',
itemId: 'panel',
layout: 'fit',
defaults: set_defaults(0),
items: {
xtype: 'panel',
title: ']project-open[ Sync 1.0.0',
defaults: set_defaults(1),
layout: 'fit',
items: {
xtype: 'tabpanel',
defaults: set_defaults(2, {
layout: 'fit',
defaults: set_defaults(3)
}),
items: [
Ext.create('POSync.view.LoginPanel'),
{
title: 'Projects',
items: {
xtype: 'grid',
itemId: 'projects',
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}
]
}
},{
title: 'Files',
items: {
xtype: 'treepanel',
itemId: 'files',
store: Ext.create('POSync.store.Files', {
storeId: 'remoteFiles'
}),
rootVisible: false,
emptyText: 'No files available',
tbar: {
xtype: 'combobox',
fieldLabel: 'Select a project',
store: 'Projects',
queryMode: 'local',
displayField: 'full_name',
valueField: 'project_id',
editable: false,
forceSelection: true,
emptyText: 'No project selected',
},
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: 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: [
{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',
bodyPadding: 10,
/* FIXME: mailto: doesn't work. electron handles it as a local file */
html: [
']project-open[ Sync',
'version 1.0.0',
'Copyright © 2018 ]project-open[ Business Solutions, S.L.',
'Authors:',
'- Gonzalo Pérez de Olaguer Córdoba',
'- Frank Bergmann',
'This code is licensed under the GNU GPL version 2.0 or later',
'',
'' +
'Node version | ' + node_version + ' |
' +
'Chrome version | ' + chrome_version + ' |
' +
'Electron version | ' + electron_version + ' |
' +
'ExtJS version | ' + extjs_version + ' |
' +
'
'
].join('
')
}]
},
buttons: [
{text: 'Login', action: 'login'},
{text: 'Sync', action: 'sync'}
]
}
/* End of POSync.view.Viewport */
});
/************************************************************
*
* Application
*
************************************************************/
var app = Ext.application({
name: 'POSync',
id: 'POSync',
itemId: 'POSync',
autoCreateViewport: true,
models: ['Project', 'File', 'Operation'],
stores: ['Projects', 'Files', 'Operations'],
controllers: ['Gui', 'Connection'],
})
// vi: set ts=8 sts=4 sw=4 et :
intranet-rest-fs-openacs-b5-0-3-patches/posync/controller/ 0000775 0000000 0000000 00000000000 13604101074 0023631 5 ustar 00root root 0000000 0000000 intranet-rest-fs-openacs-b5-0-3-patches/posync/controller/Connection.js 0000664 0000000 0000000 00000052120 13604101074 0026266 0 ustar 00root root 0000000 0000000 /************************************************************
*
* Connection Controller
*
************************************************************/
Ext.define('POSync.controller.Connection', {
extend: 'Ext.app.Controller',
id: 'Connection',
refs: [
{ ref: 'panel', selector: '#panel'},
{ ref: 'projects', selector: '#panel #projects'},
{ ref: 'files', selector: '#panel #files'},
{ ref: 'operations', selector: '#panel #operations'},
{ ref: 'localDir', selector: '#panel #localdir'},
],
/* This is called before the viewport is created */
init: function() {
console.log ('Connection.init');
this.control({
});
},
/* This is called after the viewport is created */
onLaunch: function() {
var me = this;
console.log ('Connection.onLaunch: controller onLaunch');
this.projectsStore = this.getProjects().getStore();
this.filesStore = this.getFiles().getStore();
this.localDirStore = this.getLocalDir().getStore();
this.operationsStore = Ext.StoreManager.get('Operations');
var app = this.getApplication();
this.guiController = app.getController('Gui');
// receives an even when clicking "sync" on Tray icon
ipcRenderer.on('sync', (event, arg) => {
console.log('sync: '+arg);
me.sync();
});
},
/**
* Convert a time to a PostgreSQL ISO date string
*/
timeToIso: function(time) {
var date = new Date(time);
return date.toISOString().replace("T", " ").replace("Z", "");
},
/* FIXME: no easy way to know if login is successful */
login: function (request_options) {
var me = this;
console.log('Connection.login');
/* Send the login request */
var configData = this.guiController.configData;
console.log ('Connection.login: url('+configData.url+') email('+configData.email+') password('+configData.password+')');
Ext.Ajax.request({
url: configData.url + '/intranet/auto-login-token',
params: {
email: configData.email,
password: configData.password
},
callback: function (options, success, response) {
if (!success) {
console.log('Connection.login: not successfull');
// FIXME: Write some message somewhere
return;
}
var responseJsonString = response.responseText;
var responseObject = JSON.parse(responseJsonString);
if (!responseObject) {
console.log('Connection.login: error parsing responseText='+responseText);
// FIXME: Write some message somewhere
return;
}
var token = responseObject.token;
if (!token) {
console.log('Connection.login: no token in responseText='+responseText);
// FIXME: Write some message somewhere
return;
}
console.log('Connection.login: token='+token);
configData.token = token;
// load the list of projects available to the user
me.loadProjects();
}
});
},
/* Send a request to get the list of projects */
loadProjects: function () {
var me = this;
console.log('Connection.loadProjects:');
// Check if already there
if (this.projectsStore.isLoaded) return;
var configData = this.guiController.configData;
var proxy = this.projectsStore.getProxy();
var baseUrl = configData.url;
proxy.url = baseUrl+"/intranet-rest/im_project";
proxy.extraParams = {
format: 'json',
query: 'parent_id is NULL' // Select only top-level projects
};
this.projectsStore.load({
callback: function (records,operation,success) {
if (!success) {
console.log('Connection.loadProjects: not successfull');
// FIXME: Write some message somewhere
return;
}
me.projectsStore.isLoaded = success; // mark the store as loaded
}
});
},
/* Send requests to load the files of all projects */
loadFiles: function (projectId, successCallback) {
var me = this;
console.log('Gui.loadFiles: project_id='+projectId);
// Skip if already loaded
//if (''+me.filesStore.isLoaded == ''+projectId) return;
var configData = this.guiController.configData;
var proxy = me.filesStore.getProxy();
var baseUrl = configData.url;
proxy.url = baseUrl+"/intranet-rest-fs-openacs/file-tree.json";
proxy.extraParams = {
format: 'json',
object_id: projectId
};
me.filesStore.load({
callback: function (records,operation,success) {
if (!success) {
console.log('Connection.login: not successfull');
// FIXME: Write some message somewhere
return;
}
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);
}
});
},
/*
* Get the local directory data and load the LocalFiles store.
* directory-tree provides a tree with the following fields:
*/
loadLocalDirectory: function (projectId) {
console.log('Connection.loadLocalDirectory: '+projectId);
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');
// make sure the path exists
var fs = require('fs');
if (!fs.existsSync(path)) { fs.mkdir(path); }
// Get the contents
var dirTree = require ('directory-tree');
var tree = dirTree(path,{attributes:['mode', 'mtime', 'mtimeMs']});
this.fixLocalTree(tree, projectId);
this.localDirStore.loadTree (tree);
this.localDirStore.isLoaded = projectId;
},
/**
* Fix a tree before loading it
*/
fixLocalTree: function (tree, projectId) {
var me = this;
// Get the project path
var fs = require('fs');
var sha1File = require('sha1-file');
if (tree) {
if (tree.type == 'directory') {
tree.type = 'folder';
tree.size = '';
tree.expandable = true;
tree.expanded = tree.children && tree.children.length > 0;
tree.leaf = false;
tree.mime_type = '';
} else {
var stats = fs.statSync(tree.path);
tree.sha1 = sha1File(tree.path);
tree.modification_date = me.timeToIso(stats.mtimeMs);
tree.creation_date = me.timeToIso(stats.birthtimeMs);
tree.size = stats.size;
tree.expandable = false;
tree.expanded = false;
tree.leaf = true;
tree.mime_type = tree.type;
}
tree.iconCls = 'icon-' + tree.type;
if (tree.children) {
var me = this;
tree.children.forEach (function(child) {
me.fixLocalTree (child);
});
}
}
},
/**
* Start the synchronization
* by loading the files store from the server.
*/
sync: function (projectId) {
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.syncFilesLoadedCallback);
},
/**
* The list of files has arrived.
* Now we can start with the actual sync
*/
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();
root.cascadeBy(function(remoteFile) {
if ("root" == remoteFile.get('id')) return; // Exclude folders and the root
var pathArray = remoteFile.getPathArray();
var purePathArray = pathArray.slice(0);
var fileName = remoteFile.get('name');
pathArray.push(fileName);
console.log('Connection.sync remote: path='+pathArray);
var type = remoteFile.get('type');
switch (type) {
case 'file':
var localFile = me.localDirStore.searchUsingPathArray(pathArray);
if (!localFile) {
var operation = Ext.create('POSync.model.Operation', {
id: 'download_'+pathArray.join('_'),
file_id: remoteFile.get('id'),
file_path: purePathArray.join('/'),
file_name: remoteFile.get('name'),
mime_type: remoteFile.get('mime_type'),
file_sha1: null,
local_file: localFile,
remote_file: remoteFile,
op: 'download'
});
me.operationsStore.add(operation);
} else {
// Compare local with remote file based on meta information
me.syncFileLocalRemote(projectId, localFile, remoteFile);
}
break;
case 'folder':
var localFile = me.localDirStore.searchUsingPathArray(purePathArray);
if (!localFile) {
// Create a new local folder
var fs = require('fs');
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);
}
});
// ********************************************************************
// Go through the list of local files and check that a remote file exists
var root = me.localDirStore.getRootNode();
root.cascadeBy(function(localFile) {
if ("root" == localFile.get('id')) return; // Exclude root
var pathArray = localFile.getPathArray();
var purePathArray = pathArray.slice(0);
var fileName = localFile.get('name');
pathArray.push(fileName);
console.log('Connection.sync local: path='+pathArray);
var type = localFile.get('type');
switch (type) {
case 'file':
var remoteFile = me.filesStore.searchUsingPathArray(pathArray);
if (!remoteFile) {
var operation = Ext.create('POSync.model.Operation', {
id: 'upload_'+pathArray.join('_'),
file_id: localFile.get('id'),
file_path: purePathArray.join('/'),
file_name: localFile.get('name'),
mime_type: localFile.get('mime_type'),
file_sha1: null,
local_file: localFile,
remote_file: remoteFile,
op: 'upload'
});
me.operationsStore.add(operation);
console.log('Connection.sync local: upload: localFile='+pathArray.join('/'));
} else {
// We already call syncFileLocalRemote with remote files above
// me.syncFileLocalRemote(localFile, remoteFile);
}
break;
case 'folder':
var remoteFile = me.filesStore.searchUsingPathArray(purePathArray);
if (!remoteFile) {
var operation = Ext.create('POSync.model.Operation', {
id: 'mkdir_'+pathArray.join('_'),
file_id: file.get('id'),
file_path: purePathArray.join('/'),
file_name: file.get('name'),
local_file: localFile,
remote_file: remoteFile,
op: 'mkdir'
});
me.operationsStore.add(operation);
console.log('Connection.sync local: mkdir: path='+purePathArray.join('/'));
}
break;
default: alert('sync: invalid type for local file: '+type);
}
});
me.syncExecOperations(projectId);
},
/**
* Handles the case that a file is available both locally
* and remotely.
* Checks for creation_date, file_length and compares them
* against the version history of the file on the server.
*/
syncFileLocalRemote: function(projectId, localFile, remoteFile) {
var me = this;
console.log('Connection.syncFileLocalRemote: local='+localFile.get('name')+', '+remoteFile.get('name'));
var configData = this.guiController.configData;
var project = me.projectsStore.getById(''+projectId);
var projectPath = configData.topdir+'/'+project.get('project_path');
var localPathArray = localFile.getPathArray();
var fileName = localFile.get('name');
localPathArray.push(fileName);
var localFullPath = projectPath+'/'+localPathArray.join('/');
// Check that meta information is available with the local file
var fs = require('fs');
var local_creation_date = localFile.get('creation_date');
if ("" === local_creation_date) {
var stats = fs.statSync(localFullPath);
localFile.set('creation_date', me.timeToIso(stats.birthtimeMs));
localFile.set('modification_date', me.timeToIso(stats.mtimeMs));
localFile.set('content_length', stats.size);
}
var sha1File = require('sha1-file');
var sha1 = sha1File(localFullPath);
localFile.set('sha1', sha1);
console.log('Connection.syncFileLocalRemote: finished');
},
syncFileLocalRemoteStats: function(projectId,localFile,remoteFile) {
alert(localFile);
},
/**
* Takes a store with sync "Operations" and executes them.
* This performs the actual sync.
*/
syncExecOperations: function(projectId) {
var me = this;
console.log('Connection.syncExecOperations');
me.operationsStore.each(function(op) {
var operation = op.get('op');
console.log('Connection.syncExecOperations: op='+operation+', id='+op.get('id'));
switch (operation) {
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);
}
});
},
/**
* Download a remote file to the local file-system.
*
* Called from syncExecOperations, which in turn
* loops through the list of operations created during
* sync.
*/
syncDownload: function(projectId, op) {
var me = this;
var configData = this.guiController.configData;
var fileId = op.get('file_id');
var filePath = op.get('file_path');
var fileName = op.get('file_name');
var mimeType = op.get('mime_type');
console.log('Connection.syncDownload: path='+filePath+', name='+fileName);
// Get the project path
var project = this.projectsStore.getById(''+projectId);
var projectPath = configData.topdir+'/'+project.get('project_path');
var path = projectPath+'/'+filePath;
// make sure the path exists
var fs = require('fs');
if (!fs.existsSync(path)) { fs.mkdir(path); }
var requestUrl = configData.url + '/intranet-rest-fs-openacs/download?file_id='+fileId;
var xhr = new XMLHttpRequest();
xhr.open("GET", requestUrl);
xhr.responseType = "arraybuffer";
// xhr.responseType = "blob";
xhr.onload = function () {
// Asynchronously saving files after they have been downloaded
if (this.status === 200) {
console.log('Connection.syncDownload: successful download - saving');
// Decode the response encoded by server in base64
var responseBuffer = xhr.response;
var buf64 = new Buffer(responseBuffer, 'base64'); // decode
// make sure the path exists and write the file
var fs = require('fs');
if (!fs.existsSync(path)) { fs.mkdir(path); }
fs.writeFile(path+"/"+fileName, buf64, function() {});
console.log('Connection.syncDownload: written file='+path+'/'+fileName);
} else {
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;
var configData = this.guiController.configData;
var filePath = op.get('file_path');
var fileName = op.get('file_name');
var mimeType = op.get('mime_type');
var localFile = op.get('local_file');
console.log('Connection.syncUpload: path='+filePath+', name='+fileName);
// 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('creation_date', localFile.get('creation_date'));
formData.append('modification_date', localFile.get('modification_date'));
formData.append('sha1', localFile.get('sha1'));
// 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;
// POST the file to the mkdir page
var configData = this.guiController.configData;
var filePath = op.get('file_path');
console.log('Connection.syncMkdir: path='+filePath);
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 */
});
intranet-rest-fs-openacs-b5-0-3-patches/posync/controller/Gui.js 0000664 0000000 0000000 00000027661 13604101074 0024727 0 ustar 00root root 0000000 0000000 /************************************************************
*
* GUI Controller
*
************************************************************/
/*
* A single controller for the whole GUI
*/
Ext.define('POSync.controller.Gui', {
extend: 'Ext.app.Controller',
is: 'Gui',
refs: [
{ ref: 'panel', selector: '#panel'},
{ ref: 'configPanel', selector: '#panel #config'},
{ ref: 'projects', selector: '#panel #projects'},
{ ref: 'files', selector: '#panel #files'},
{ ref: 'operations', selector: '#panel #operations'},
{ ref: 'projectSelector', selector: '#panel #files combobox'},
{ ref: 'localDir', selector: '#panel #localdir'},
{ ref: 'filterSelector', selector: '#panel #syncdir combobox'}
],
/************************************************************
*
* Initialization
*
************************************************************/
/* Default values for the configuration data */
configDefaults: {
url: 'http://demo.project-open.net',
email: 'ben.bigboss@tigerpond.com',
password: 'ben',
topdir: './topdir',
},
/* Not really used right now, but canonical values of data
* should be placed somewhere.
*/
configData: {
changed: false,
url: null,
email: null,
password: null,
topdir: null,
form_is_valid: null,
selected_project_id: null,
token: null
},
/* This is called before the viewport is created */
init: function() {
console.log ('Gui.init: controller initialization');
this.control({
'#panel #config field': { change: this.onFieldChanged },
'#panel toolbar > button': { click: this.onButtonClicked },
'#panel #projects': { select: this.onProjectSelectedInProjects },
'#panel #files combobox': { select: this.onProjectSelectedInFiles },
});
},
/* This is called after the viewport is created */
onLaunch: function() {
console.log ('Gui.onLaunch: controller onLaunch');
this.projectsStore = this.getProjects().getStore();
this.filesStore = this.getFiles().getStore();
this.localDirStore = this.getLocalDir().getStore();
var app = this.getApplication();
this.connectionController = app.getController('Connection');
this.initConfiguration ();
this.recoverState ();
},
/* Initialize the configuration data */
initConfiguration: function () {
var configPanel = this.getConfigPanel();
console.log('Gui.initConfiguration: config('+configPanel.id+')');
const Store = require ('electron-store');
const configStore = new Store ({name: 'config'});
/* Initialize each configuration value to the saved one
* or to the default value if none is already saved.
*/
var defaults = this.configDefaults;
for (var name in defaults) {
var d = defaults[name];
var v = configStore.get(name, d);
var e = configPanel.down('[name='+name+']');
e.setValue(v);
this.configData[name] = v;
/* Save the configuration value whenever it changes */
e.on('change', function() {
var name = this.name;
var value = this.getValue();
console.log ('Gui.initConfiguration.onChange('+name+'): changed to '+value+') - saving to configStore');
configStore.set(name, value);
});
}
},
/* Recover the application state */
recoverState: function () {
var panel = this.getPanel();
console.log('Gui.recoverState: panel('+panel.id+')');
const Store = require ('electron-store');
const stateStore = new Store ({name: 'state'});
var w = stateStore.get('panel.width', 500);
var h = stateStore.get('panel.height', 300);
panel.setSize(w,h);
panel.on ('resize', function() {
var s = panel.getSize();
stateStore.set({panel: s});
});
},
/************************************************************
*
* Configuration data changes
*
************************************************************/
onFieldChanged: function (field, newValue, oldValue, eOpts) {
var name = field.name;
console.log ('Gui.onFieldChanged: field('+name+') changed from oldValue('+oldValue+') to newValue('+newValue+') eOpts('+eOpts+')');
if (name in this.configDefaults && this.configData[name] != newValue) {
console.log ('Gui.onFieldChanged: field('+name+') changed from('+this.configData[name]+') to('+newValue+')');
this.configData[name] = newValue;
this.configData.changed = true;
}
},
/************************************************************
*
* Button actions
*
************************************************************/
onButtonClicked: function (button, event, eOpts) {
console.log ('Gui.onButtonClicked: button clicked button('+button.getText()+') action('+button.action+') event('+event+') eOpts('+eOpts+')');
switch (button.action) {
case 'login':
console.log('Gui.onButtonClicked: login clicked');
if (this.formIsValid()) {
this.connectionController.login();
} else {
console.log('Connection.login: form is not valid - cancelling request');
alert('Form is not valid');
}
break;
case 'projects':
console.log('Gui.onButtonClicked: projects clicked');
this.connectionController.loadProjects();
break;
case 'files':
console.log('Gui.onButtonClicked: files clicked');
this.connectionController.loadFiles(45956);
break;
case 'localdir':
console.log('Gui.onButtonClicked: localdir clicked');
this.connectionController.loadLocalDirectory(45956);
break;
case 'sync':
console.log('Gui.onButtonClicked: sync clicked');
this.connectionController.sync(45956);
break;
}
},
/************************************************************
*
* Project selection
*
************************************************************/
/* Project selected by clicking a row in the projects panel */
onProjectSelectedInProjects: function (row, record, index, eOpts) {
var id = record.get('project_id');
console.log ('Gui.onProjectSelectedInProjects: id('+id+')');
this.selectProject (record);
},
/* Project selected in the selection box in the files panel */
onProjectSelectedInFiles: function (combo, records, eOpts) {
/* The combobox must be configured for 'single' selection mode */
var id = records[0].get('project_id');
console.log ('Gui.onProjectSelectedInFiles: id('+id+')');
this.selectProject (records[0]);
},
/* Select the specified project and load its files */
selectProject: function (project) {
var id = project.get('project_id');
console.log('Gui.selectProject (project) id('+id+')');
console.log(project);
if (this.configData.selected_project_id != id) {
this.configData.selected_project_id = id;
/* Update the selected row in the projects panel */
this.getProjects().getSelectionModel().select (project);
/* Update the selected project in the combobox of the files panel */
this.getProjectSelector().select(project);
/* Load the files of the selected project */
var me = this;
project.getFile(function(file,operation){
me.selectedProjectFilesLoaded (project,file,operation);
});
}
},
/* Called whenever the list of files of the selected project have been received */
selectedProjectFilesLoaded: function (project,file,operation) {
var pid = project ? project.get('project_id') : 'undef';
var fid = file ? file.get('id') : 'undef';
console.log('Gui.selectedProjectFilesLoaded(project,file,operation): project_id('+pid+') file_id('+fid+')');
console.log(project);
console.log(file);
console.log(operation);
/* Show the files in the Files panel */
this.getFiles().setRootNode(file);
/* Update sync remote project files */
this.syncRemoteFiles (project,file);
},
/************************************************************
*
* Filter selection
*
************************************************************/
/* Project selected in the selection box in the files panel */
onFilterSelected: function (combo, records, eOpts) {
/* The combobox must be configured for 'single' selection mode */
var id = records[0].get('id');
console.log ('Gui.onFilterSelected(combo,records,eOpts): id('+id+')');
console.log(combo);
console.log(records);
console.log(eOpts);
this.filterSyncNodes (id);
},
/* Select the specified project and load its files
*
* WARNING: esto ha sido un puto infierno. He probado MUCHAS formas
* de hacerlo y todas fallaban por una u otra razón. Ésta parece que funciona.
*/
filterSyncNodes: function (id) {
console.log('Gui.filterSyncNodes: id('+id+')');
var view = this.getSyncDir().getView();
this.syncDirStore.forEachRecord (function (record) {
var v = record.filteredBy (id);
var el = Ext.get(view.getNode (record));
/* el is defined only for currently expanded nodes */
if (el)
el.setDisplayed(v);
});
},
/* Check if the form data is valid */
formIsValid: function () {
var config = this.getConfigPanel();
this.configData.url = config.down('[name=url]').getValue();
this.configData.email = config.down('[name=email]').getValue();
this.configData.password = config.down('[name=password]').getValue();
this.configData.topdir = config.down('[name=topdir]').getValue();
this.configData.form_is_valid = config.getForm().isValid();
var msg = [
'form ' + (this.configData.form_is_valid ? 'is' : 'is not') + ' valid and '
+ (this.configData.changed ? 'is' : 'is not') + ' changed',
'URL : "' + this.configData.url + '"',
'Email : "' + this.configData.email + '"',
'Password: "' + this.configData.password + '"',
'Topdir : "' + this.configData.topdir + '"'
].join('\n');
console.log (msg);
return this.configData.form_is_valid;
},
/************************************************************
*
* Window management
*
************************************************************/
/* Hide the window but keep the tray icon */
hideWindow: function () {
console.log('Gui.hideWindow');
const { remote } = require('electron');
/* FIXME: should be the Gui window of this controller's application */
remote.BrowserWindow.getFocusedWindow().hide();
},
/* Close the window and remove the tray icon */
closeWindow: function () {
console.log('Gui.closeWindow');
const { remote } = require('electron');
/* FIXME: should be the Gui window of this controller's application */
remote.BrowserWindow.getFocusedWindow().close();
},
});
intranet-rest-fs-openacs-b5-0-3-patches/posync/images/ 0000775 0000000 0000000 00000000000 13604101074 0022713 5 ustar 00root root 0000000 0000000 intranet-rest-fs-openacs-b5-0-3-patches/posync/images/tray.png 0000664 0000000 0000000 00000005356 13604101074 0024411 0 ustar 00root root 0000000 0000000 PNG
IHDR 0 0 W sBIT|d pHYs
B(x tEXtSoftware www.inkscape.org< tEXtTitle Terminal tEXtAuthor Lapo Calamandreiߑ* )tEXtDescription Based of Jakub Steiner's work'' IDAThKl$y_ULsM*ZqWڕVv$ IN1|Gl@n K |%\|3rHnQd+Jkg8\k"
]UYo<ϻE;f_?}Dw10;yhÍ_#p>͚1"1k-Ƙt-"xqOw9&$zwE.HܼygTU%2oڠ/>p>I EQPl[ҵ[)"k wƍ[/stO)˒(/}{ ;R^(