diff --git a/public/res/constants.js b/public/res/constants.js index fa822308..8c433bdb 100644 --- a/public/res/constants.js +++ b/public/res/constants.js @@ -27,7 +27,7 @@ define([], function() { constants.PICASA_PROXY_URL = "https://stackedit-picasa-proxy.herokuapp.com/"; constants.SSH_PROXY_URL = "https://stackedit-ssh-proxy.herokuapp.com/"; constants.HTMLTOPDF_URL = "https://stackedit-htmltopdf.herokuapp.com/"; - constants.TEAM_SERVER_URL = "http://localhost:11583/"; + constants.TEAM_SERVER_URL = "/"; // Site dependent constants.BASE_URL = "http://localhost/"; diff --git a/public/res/helpers/googleHelper.js b/public/res/helpers/googleHelper.js index d6543043..bd1607e1 100644 --- a/public/res/helpers/googleHelper.js +++ b/public/res/helpers/googleHelper.js @@ -1,960 +1,981 @@ /*global gapi, google */ define([ - "underscore", - "jquery", - "constants", - "core", - "utils", - "storage", - "logger", - "settings", - "eventMgr", - "classes/AsyncTask", + "underscore", + "jquery", + "constants", + "core", + "utils", + "storage", + "logger", + "settings", + "eventMgr", + "classes/AsyncTask", ], function(_, $, constants, core, utils, storage, logger, settings, eventMgr, AsyncTask) { - var connected = false; - var authorizationMgrMap = {}; - function AuthorizationMgr(accountId) { - var permissionList = { - profile: true - }; - var refreshFlag = true; - _.each((storage[accountId + '.permissions'] || '').split(';'), function(permission) { - permission && (permissionList[permission] = true); - }); - this.setRefreshFlag = function() { - refreshFlag = true; - }; - this.isAuthorized = function(permission) { - return refreshFlag === false && _.has(permissionList, permission); - }; - this.add = function(permission) { - permissionList[permission] = true; - storage[accountId + '.permissions'] = _.keys(permissionList).join(';'); - refreshFlag = false; - }; - this.getListWithNew = function(permission) { - var result = _.keys(permissionList); - if(!_.has(permissionList, permission)) { - result.push(permission); - } - return result; - }; - var userId = storage[accountId + '.userId']; - this.setUserId = function(value) { - userId = value; - storage[accountId + '.userId'] = userId; - }; - this.getUserId = function() { - return userId; - }; - } + var connected = false; + var authorizationMgrMap = {}; - var googleHelper = {}; + function AuthorizationMgr(accountId) { + var permissionList = { + profile: true + }; + var refreshFlag = true; + _.each((storage[accountId + '.permissions'] || '').split(';'), function(permission) { + permission && (permissionList[permission] = true); + }); + this.setRefreshFlag = function() { + refreshFlag = true; + }; + this.isAuthorized = function(permission) { + return refreshFlag === false && _.has(permissionList, permission); + }; + this.add = function(permission) { + permissionList[permission] = true; + storage[accountId + '.permissions'] = _.keys(permissionList).join(';'); + refreshFlag = false; + }; + this.getListWithNew = function(permission) { + var result = _.keys(permissionList); + if(!_.has(permissionList, permission)) { + result.push(permission); + } + return result; + }; + var userId = storage[accountId + '.userId']; + this.setUserId = function(value) { + userId = value; + storage[accountId + '.userId'] = userId; + }; + this.getUserId = function() { + return userId; + }; + } - // Listen to offline status changes - var isOffline = false; - eventMgr.addListener("onOfflineChanged", function(isOfflineParam) { - isOffline = isOfflineParam; - }); + var googleHelper = {}; - // Try to connect by downloading client.js - function connect(task) { - task.onRun(function() { - if(isOffline === true) { - connected = false; - return task.error(new Error("Operation not available in offline mode.|stopPublish")); - } - if(connected === true) { - return task.chain(); - } - window.delayedFunction = function() { - gapi.load("client", function() { - gapi.client.load('drive', 'v2', function() { - connected = true; - task.chain(); - }); - }); - }; - $.ajax({ - url: "https://apis.google.com/js/api.js?onload=runDelayedFunction", - dataType: "script", - timeout: constants.AJAX_TIMEOUT - }).fail(function(jqXHR) { - var error = { - code: jqXHR.status, - message: jqXHR.statusText - }; - handleError(error, task); - }); - }); - } + // Listen to offline status changes + var isOffline = false; + eventMgr.addListener("onOfflineChanged", function(isOfflineParam) { + isOffline = isOfflineParam; + }); - // Try to authenticate with Oauth - var scopeMap = { - profile: [ - 'https://www.googleapis.com/auth/userinfo.profile' - ], - gdrive: [ - 'https://www.googleapis.com/auth/drive.install', - settings.gdriveFullAccess === true ? 'https://www.googleapis.com/auth/drive' : 'https://www.googleapis.com/auth/drive.file' - ], - blogger: [ - 'https://www.googleapis.com/auth/blogger' - ], - picasa: [ - 'https://www.googleapis.com/auth/photos' - ] - }; - function authenticate(task, permission, accountId) { - var authorizationMgr = authorizationMgrMap[accountId]; - if(!authorizationMgr) { - authorizationMgr = new AuthorizationMgr(accountId); - authorizationMgrMap[accountId] = authorizationMgr; - } - task.onRun(function() { - var currentToken = gapi.auth.getToken(); - var newToken; - function getTokenInfo() { - $.ajax({ - url: 'https://www.googleapis.com/oauth2/v1/tokeninfo', - data: { - access_token: newToken.access_token - }, - timeout: constants.AJAX_TIMEOUT, - type: "GET" - }).done(function(data) { - if(authorizationMgr.getUserId() && authorizationMgr.getUserId() != data.user_id) { - // Wrong user id, try again - startAuthenticate(); - } - else { - authorizationMgr.setUserId(data.user_id); - authorizationMgr.add(permission); - authorizationMgr.token = newToken; - task.chain(); - } - }).fail(function(jqXHR) { - var error = { - code: jqXHR.status, - message: jqXHR.statusText - }; - handleError(error, task); - }); - } - var authuser = 0; - var immediate; - function localAuthenticate() { - if(authuser > 5) { - return task.error(new Error('Unable to authenticate user ' + authorizationMgr.getUserId() + ', please sign in with Google.')); - } - if(immediate === false) { - task.timeout = constants.ASYNC_TASK_LONG_TIMEOUT; - } - var scopeList = _.chain(scopeMap).pick(authorizationMgr.getListWithNew(permission)).flatten().value(); - gapi.auth.authorize({ - client_id: constants.GOOGLE_CLIENT_ID, - scope: scopeList, - immediate: immediate, - authuser: immediate === false ? '' : authuser - }, function(authResult) { - newToken = gapi.auth.getToken(); - gapi.auth.setToken(currentToken); - if(!authResult || authResult.error) { - if(connected === true && immediate === true) { - // If immediate did not work retry without immediate - // flag - immediate = false; - task.chain(oauthRedirect); - } - else { - // Error - task.error(new Error("Access to Google account is not authorized.")); - } - } - else { - // Success but we need to check the user id - immediate === true && authuser++; - task.chain(getTokenInfo); - } - }); - } - function oauthRedirect() { - if(immediate === true) { - return task.chain(localAuthenticate); - } - utils.redirectConfirm('You are being redirected to Google authorization page.', function() { - task.chain(localAuthenticate); - }, function() { - task.error(new Error('Operation canceled.')); - }); - } - function startAuthenticate() { - immediate = true; - if(authorizationMgr.token && authorizationMgr.isAuthorized(permission)) { - task.chain(); - return; - } - if(!authorizationMgr.getUserId()) { - immediate = false; - } - task.chain(oauthRedirect); - } - startAuthenticate(); - }); - } - googleHelper.refreshGdriveToken = function(accountId) { - var task = new AsyncTask(); - connect(task); - var authorizationMgr = authorizationMgrMap[accountId]; - authorizationMgr && authorizationMgr.setRefreshFlag(); - authenticate(task, 'gdrive', accountId); - task.enqueue(); - }; + // Try to connect by downloading client.js + function connect(task) { + task.onRun(function() { + if(isOffline === true) { + connected = false; + return task.error(new Error("Operation not available in offline mode.|stopPublish")); + } + if(connected === true) { + return task.chain(); + } + window.delayedFunction = function() { + gapi.load("client", function() { + gapi.client.load('drive', 'v2', function() { + connected = true; + task.chain(); + }); + }); + }; + $.ajax({ + url: "https://apis.google.com/js/api.js?onload=runDelayedFunction", + dataType: "script", + timeout: constants.AJAX_TIMEOUT + }).fail(function(jqXHR) { + var error = { + code: jqXHR.status, + message: jqXHR.statusText + }; + handleError(error, task); + }); + }); + } - function runWithToken(accountId, functionToRun) { - var currentToken = gapi.auth.getToken(); - var authorizationMgr = authorizationMgrMap[accountId]; - gapi.auth.setToken(authorizationMgr.token); - functionToRun(); - gapi.auth.setToken(currentToken); - } + // Try to authenticate with Oauth + var scopeMap = { + profile: [ + 'https://www.googleapis.com/auth/userinfo.profile' + ], + gdrive: [ + 'https://www.googleapis.com/auth/drive.install', + settings.gdriveFullAccess === true ? 'https://www.googleapis.com/auth/drive' : 'https://www.googleapis.com/auth/drive.file' + ], + blogger: [ + 'https://www.googleapis.com/auth/blogger' + ], + picasa: [ + 'https://www.googleapis.com/auth/photos' + ] + }; - googleHelper.upload = function(fileId, parentId, title, content, contentType, etag, accountId, callback) { - var result; - var task = new AsyncTask(); - connect(task); - authenticate(task, 'gdrive', accountId); - task.onRun(function() { - var boundary = '-------314159265358979323846'; - var delimiter = "\r\n--" + boundary + "\r\n"; - var close_delim = "\r\n--" + boundary + "--"; - contentType = contentType || settings.markdownMimeType; - var metadata = { - title: title, - mimeType: contentType - }; - if(parentId) { - // Specify the directory - metadata.parents = [ - { - kind: 'drive#fileLink', - id: parentId - } - ]; - } - var path = '/upload/drive/v2/files'; - var method = 'POST'; - if(fileId) { - // If it's an update - path += "/" + fileId; - method = 'PUT'; - } - var headers = { - 'Content-Type': 'multipart/mixed; boundary="' + boundary + '"', - }; + function authenticate(task, permission, accountId) { + var authorizationMgr = authorizationMgrMap[accountId]; + if(!authorizationMgr) { + authorizationMgr = new AuthorizationMgr(accountId); + authorizationMgrMap[accountId] = authorizationMgr; + } + task.onRun(function() { + var currentToken = gapi.auth.getToken(); + var newToken; - var base64Data = utils.encodeBase64(content); - var multipartRequestBody = [ - delimiter, - 'Content-Type: application/json\r\n\r\n', - JSON.stringify(metadata), - delimiter, - 'Content-Type: ', - contentType, - '\r\n', - 'Content-Transfer-Encoding: base64\r\n', - '\r\n', - base64Data, - close_delim - ].join(""); + function getTokenInfo() { + $.ajax({ + url: 'https://www.googleapis.com/oauth2/v1/tokeninfo', + data: { + access_token: newToken.access_token + }, + timeout: constants.AJAX_TIMEOUT, + type: "GET" + }).done(function(data) { + if(authorizationMgr.getUserId() && authorizationMgr.getUserId() != data.user_id) { + // Wrong user id, try again + startAuthenticate(); + } + else { + authorizationMgr.setUserId(data.user_id); + authorizationMgr.add(permission); + authorizationMgr.token = newToken; + task.chain(); + } + }).fail(function(jqXHR) { + var error = { + code: jqXHR.status, + message: jqXHR.statusText + }; + handleError(error, task); + }); + } - runWithToken(accountId, function() { - var request = gapi.client.request({ - 'path': path, - 'method': method, - 'params': { - 'uploadType': 'multipart', - }, - 'headers': headers, - 'body': multipartRequestBody, - }); - request.execute(function(response) { - if(response && response.id) { - // Upload success - result = response; - result.content = content; - return task.chain(); - } - var error = response.error; - // Handle error - if(error !== undefined && fileId !== undefined) { - if(error.code === 404) { - error = 'File ID "' + fileId + '" not found on Google Drive.|removePublish'; - } - else if(error.code === 412) { - // We may have missed a file update - storage.removeItem(accountId + ".gdrive.lastChangeId"); - error = 'Conflict on file ID "' + fileId + '". Please restart the synchronization.'; - } - } - handleError(error, task); - }); - }); - }); - task.onSuccess(function() { - callback(undefined, result); - }); - task.onError(function(error) { - callback(error); - }); - task.enqueue(); - }; + var authuser = 0; + var immediate; - googleHelper.rename = function(fileId, title, accountId, callback) { - var result; - var task = new AsyncTask(); - connect(task); - authenticate(task, 'gdrive', accountId); - task.onRun(function() { - var body = {'title': title}; - runWithToken(accountId, function() { - var request = gapi.client.drive.files.patch({ - 'fileId': fileId, - 'resource': body - }); - request.execute(function(response) { - if(response && response.id) { - // Rename success - result = response; - return task.chain(); - } - var error = response.error; - // Handle error - if(error !== undefined && fileId !== undefined) { - if(error.code === 404) { - error = 'File ID "' + fileId + '" not found on Google Drive.|removePublish'; - } - } - handleError(error, task); - }); - }); - }); - task.onSuccess(function() { - callback(undefined, result); - }); - task.onError(function(error) { - callback(error); - }); - task.enqueue(); - }; + function localAuthenticate() { + if(authuser > 5) { + return task.error(new Error('Unable to authenticate user ' + authorizationMgr.getUserId() + ', please sign in with Google.')); + } + if(immediate === false) { + task.timeout = constants.ASYNC_TASK_LONG_TIMEOUT; + } + var scopeList = _.chain(scopeMap).pick(authorizationMgr.getListWithNew(permission)).flatten().value(); + gapi.auth.authorize({ + client_id: constants.GOOGLE_CLIENT_ID, + scope: scopeList, + immediate: immediate, + authuser: immediate === false ? '' : authuser + }, function(authResult) { + newToken = gapi.auth.getToken(); + gapi.auth.setToken(currentToken); + if(!authResult || authResult.error) { + if(connected === true && immediate === true) { + // If immediate did not work retry without immediate + // flag + immediate = false; + task.chain(oauthRedirect); + } + else { + // Error + task.error(new Error("Access to Google account is not authorized.")); + } + } + else { + // Success but we need to check the user id + immediate === true && authuser++; + task.chain(getTokenInfo); + } + }); + } - googleHelper.checkChanges = function(lastChangeId, accountId, callback) { - var changes = []; - var newChangeId = lastChangeId || 0; - var task = new AsyncTask(); - connect(task); - authenticate(task, 'gdrive', accountId); - task.onRun(function() { - var nextPageToken; - function retrievePageOfChanges() { - runWithToken(accountId, function() { - var request; - if(nextPageToken === undefined) { - request = gapi.client.drive.changes.list({ - 'startChangeId': newChangeId + 1 - }); - } - else { - request = gapi.client.drive.changes.list({ - 'pageToken': nextPageToken - }); - } + function oauthRedirect() { + if(immediate === true) { + return task.chain(localAuthenticate); + } + utils.redirectConfirm('You are being redirected to Google authorization page.', function() { + task.chain(localAuthenticate); + }, function() { + task.error(new Error('Operation canceled.')); + }); + } - request.execute(function(response) { - if(!response || !response.largestChangeId) { - // Handle error - return handleError(response.error, task); - } - // Retrieve success - newChangeId = response.largestChangeId; - nextPageToken = response.nextPageToken; - if(response.items !== undefined) { - changes = changes.concat(response.items); - } - if(nextPageToken !== undefined) { - task.chain(retrievePageOfChanges); - } - else { - task.chain(); - } - }); - }); - } - task.chain(retrievePageOfChanges); - }); - task.onSuccess(function() { - callback(undefined, changes, newChangeId); - }); - task.onError(function(error) { - callback(error); - }); - task.enqueue(); - }; + function startAuthenticate() { + immediate = true; + if(authorizationMgr.token && authorizationMgr.isAuthorized(permission)) { + task.chain(); + return; + } + if(!authorizationMgr.getUserId()) { + immediate = false; + } + task.chain(oauthRedirect); + } - googleHelper.downloadMetadata = function(ids, accountId, callback, skipAuth) { - var result = []; - var task = new AsyncTask(); - connect(task); - if(!skipAuth) { - authenticate(task, 'gdrive', accountId); - } - task.onRun(function() { - function recursiveDownloadMetadata() { - if(ids.length === 0) { - return task.chain(); - } - var id = ids[0]; - var headers = {}; - var authorizationMgr = authorizationMgrMap[accountId]; - if(authorizationMgr && authorizationMgr.token) { - headers.Authorization = "Bearer " + authorizationMgr.token.access_token; - } - $.ajax({ - url: "https://www.googleapis.com/drive/v2/files/" + id, - headers: headers, - data: { - key: constants.GOOGLE_API_KEY - }, - dataType: "json", - timeout: constants.AJAX_TIMEOUT - }).done(function(data) { - result.push(data); - ids.shift(); - task.chain(recursiveDownloadMetadata); - }).fail(function(jqXHR) { - var error = { - code: jqXHR.status, - message: jqXHR.statusText - }; - // Handle error - if(error.code === 404) { - error = 'File ID "' + id + '" not found on Google Drive.'; - } - handleError(error, task); - }); - } - task.chain(recursiveDownloadMetadata); - }); - task.onSuccess(function() { - callback(undefined, result); - }); - task.onError(function(error) { - callback(error); - }); - task.enqueue(); - }; + startAuthenticate(); + }); + } - googleHelper.downloadContent = function(objects, accountId, callback, skipAuth) { - var result = []; - var task = new AsyncTask(); - // Add some time for user to choose his files - task.timeout = constants.ASYNC_TASK_LONG_TIMEOUT; - connect(task); - if(!skipAuth) { - authenticate(task, 'gdrive', accountId); - } - task.onRun(function() { - function recursiveDownloadContent() { - if(objects.length === 0) { - return task.chain(); - } - var object = objects[0]; - result.push(object); - var file; - // object may be a file - if(object.kind == "drive#file") { - file = object; - } - // object may be a change - else if(object.kind == "drive#change") { - file = object.file; - } - if(!file) { - objects.shift(); - return task.chain(recursiveDownloadContent); - } - var url = file.downloadUrl; - // if file is a real time document - if(file.mimeType.indexOf("application/vnd.google-apps.drive-sdk") === 0) { - file.isRealtime = true; - url = 'https://www.googleapis.com/drive/v2/files/' + file.id + '/realtime'; - } - var headers = {}; - var authorizationMgr = authorizationMgrMap[accountId]; - if(authorizationMgr && authorizationMgr.token) { - headers.Authorization = "Bearer " + authorizationMgr.token.access_token; - } - $.ajax({ - url: url, - headers: headers, - data: { - key: constants.GOOGLE_API_KEY - }, - dataType: file.isRealtime ? 'json' : 'text', - timeout: constants.AJAX_TIMEOUT - }).done(function(data) { - file.content = file.isRealtime ? data.data.value.content.value : data; - objects.shift(); - task.chain(recursiveDownloadContent); - }).fail(function(jqXHR) { - var error = { - code: jqXHR.status, - message: jqXHR.statusText - }; - // Handle error - handleError(error, task); - }); - } - task.chain(recursiveDownloadContent); - }); - task.onSuccess(function() { - callback(undefined, result); - }); - task.onError(function(error) { - callback(error); - }); - task.enqueue(); - }; + googleHelper.refreshGdriveToken = function(accountId) { + var task = new AsyncTask(); + connect(task); + var authorizationMgr = authorizationMgrMap[accountId]; + authorizationMgr && authorizationMgr.setRefreshFlag(); + authenticate(task, 'gdrive', accountId); + task.enqueue(); + }; - googleHelper.uploadImg = function(name, content, albumId, callback) { - var accountId = 'google.picasa0'; - var result; - var task = new AsyncTask(); - connect(task); - authenticate(task, 'picasa', accountId); - task.onRun(function() { - var headers = { - "Slug": name - }; - if(name.match(/.jpe?g$/i)) { - headers["Content-Type"] = "image/jpeg"; - } - else if(name.match(/.png$/i)) { - headers["Content-Type"] = "image/png"; - } - else if(name.match(/.gif$/i)) { - headers["Content-Type"] = "image/gif"; - } - var authorizationMgr = authorizationMgrMap[accountId]; - if(authorizationMgr && authorizationMgr.token) { - headers.Authorization = "Bearer " + authorizationMgr.token.access_token; - } + function runWithToken(accountId, functionToRun) { + var currentToken = gapi.auth.getToken(); + var authorizationMgr = authorizationMgrMap[accountId]; + gapi.auth.setToken(authorizationMgr.token); + functionToRun(); + gapi.auth.setToken(currentToken); + } - $.ajax({ - url: constants.PICASA_PROXY_URL + "upload/" + albumId, - headers: headers, - data: content, - processData: false, - dataType: "xml", - timeout: constants.AJAX_TIMEOUT, - type: "POST" - }).done(function(data) { - result = data; - task.chain(); - }).fail(function(jqXHR) { - var error = { - code: jqXHR.status, - message: jqXHR.statusText - }; - if(error.code == 200) { - error.message = jqXHR.responseText; - } - handleError(error, task); - }); - }); - task.onSuccess(function() { - callback(undefined, result); - }); - task.onError(function(error) { - callback(error); - }); - task.enqueue(); - }; + googleHelper.upload = function(fileId, parentId, title, content, contentType, etag, accountId, callback) { + var result; + var task = new AsyncTask(); + connect(task); + authenticate(task, 'gdrive', accountId); + task.onRun(function() { + var boundary = '-------314159265358979323846'; + var delimiter = "\r\n--" + boundary + "\r\n"; + var close_delim = "\r\n--" + boundary + "--"; + contentType = contentType || settings.markdownMimeType; + var metadata = { + title: title, + mimeType: contentType + }; + if(parentId) { + // Specify the directory + metadata.parents = [ + { + kind: 'drive#fileLink', + id: parentId + } + ]; + } + var path = '/upload/drive/v2/files'; + var method = 'POST'; + if(fileId) { + // If it's an update + path += "/" + fileId; + method = 'PUT'; + } + var headers = { + 'Content-Type': 'multipart/mixed; boundary="' + boundary + '"', + }; - function handleError(error, task) { - var errorMsg; - if(error) { - logger.error(error); - // Try to analyze the error - if(typeof error === "string") { - errorMsg = error; - } - else { - errorMsg = "Google error (" + error.code + ": " + error.message + ")."; - if(error.code >= 500 && error.code < 600) { - // Retry as described in Google's best practices - return task.retry(new Error(errorMsg)); - } - else if(error.code === 401 || error.code === 403 || error.code == "token_refresh_required") { - _.each(authorizationMgrMap, function(authorizationMgr) { - authorizationMgr.setRefreshFlag(); - }); - errorMsg = "Access to Google account is not authorized."; - return task.retry(new Error(errorMsg), 1); - } - else if(error.code === 0 || error.code === -1) { - connected = false; - _.each(authorizationMgrMap, function(authorizationMgr) { - authorizationMgr.setRefreshFlag(); - }); - core.setOffline(); - errorMsg = "|stopPublish"; - } - } - } - task.error(new Error(errorMsg)); - } + var base64Data = utils.encodeBase64(content); + var multipartRequestBody = [ + delimiter, + 'Content-Type: application/json\r\n\r\n', + JSON.stringify(metadata), + delimiter, + 'Content-Type: ', + contentType, + '\r\n', + 'Content-Transfer-Encoding: base64\r\n', + '\r\n', + base64Data, + close_delim + ].join(""); - var pickerLoaded = false; - function loadPicker(task) { - task.onRun(function() { - if(pickerLoaded === true) { - return task.chain(); - } - $.ajax({ - url: "//www.google.com/jsapi", - data: { - key: constants.GOOGLE_API_KEY - }, - dataType: "script", - timeout: constants.AJAX_TIMEOUT - }).done(function() { - google.load('picker', '1', { - callback: function() { - task.chain(); - } - }); - pickerLoaded = true; - }).fail(function(jqXHR) { - var error = { - code: jqXHR.status, - message: jqXHR.statusText - }; - handleError(error, task); - }); - }); - } + runWithToken(accountId, function() { + var request = gapi.client.request({ + 'path': path, + 'method': method, + 'params': { + 'uploadType': 'multipart', + }, + 'headers': headers, + 'body': multipartRequestBody, + }); + request.execute(function(response) { + if(response && response.id) { + // Upload success + result = response; + result.content = content; + return task.chain(); + } + var error = response.error; + // Handle error + if(error !== undefined && fileId !== undefined) { + if(error.code === 404) { + error = 'File ID "' + fileId + '" not found on Google Drive.|removePublish'; + } + else if(error.code === 412) { + // We may have missed a file update + storage.removeItem(accountId + ".gdrive.lastChangeId"); + error = 'Conflict on file ID "' + fileId + '". Please restart the synchronization.'; + } + } + handleError(error, task); + }); + }); + }); + task.onSuccess(function() { + callback(undefined, result); + }); + task.onError(function(error) { + callback(error); + }); + task.enqueue(); + }; - googleHelper.picker = function(callback, pickerType, accountId) { - var docs = []; - var picker; - function hidePicker() { - if(picker !== undefined) { - picker.setVisible(false); - $(".modal-backdrop, .picker").remove(); - } - } - var task = new AsyncTask(); - // Add some time for user to choose his files - task.timeout = constants.ASYNC_TASK_LONG_TIMEOUT; - connect(task); - if(pickerType == 'doc' || pickerType == 'folder') { - authenticate(task, 'gdrive', accountId); - } - else { - accountId = 'google.picasa0'; - authenticate(task, 'picasa', accountId); - } - loadPicker(task); - task.onRun(function() { - var authorizationMgr = authorizationMgrMap[accountId]; - var pickerBuilder = new google.picker.PickerBuilder(); - pickerBuilder.setAppId(constants.GOOGLE_DRIVE_APP_ID); - var view; - if(pickerType == 'doc') { - view = new google.picker.DocsView(google.picker.ViewId.DOCS); - view.setParent('root'); - view.setIncludeFolders(true); - view.setMimeTypes([ - "text/x-markdown", - "text/plain", - "application/octet-stream", - "application/vnd.google-apps.drive-sdk." + constants.GOOGLE_DRIVE_APP_ID - ].join(",")); - pickerBuilder.enableFeature(google.picker.Feature.NAV_HIDDEN); - pickerBuilder.enableFeature(google.picker.Feature.MULTISELECT_ENABLED); - pickerBuilder.addView(view); - authorizationMgr && authorizationMgr.token && pickerBuilder.setOAuthToken(authorizationMgr.token.access_token); - } - else if(pickerType == 'folder') { - view = new google.picker.DocsView(google.picker.ViewId.FOLDERS); - view.setParent('root'); - view.setIncludeFolders(true); - view.setSelectFolderEnabled(true); - view.setMimeTypes('application/vnd.google-apps.folder'); - pickerBuilder.enableFeature(google.picker.Feature.NAV_HIDDEN); - pickerBuilder.addView(view); - authorizationMgr && authorizationMgr.token && pickerBuilder.setOAuthToken(authorizationMgr.token.access_token); - } - else if(pickerType == 'img') { - view = new google.picker.PhotosView(); - view.setType('flat'); - pickerBuilder.addView(view); - view = new google.picker.PhotosView(); - view.setType('ofuser'); - pickerBuilder.addView(view); - pickerBuilder.addView(google.picker.ViewId.PHOTO_UPLOAD); - authorizationMgr && authorizationMgr.token && pickerBuilder.setOAuthToken(authorizationMgr.token.access_token); - } - pickerBuilder.setCallback(function(data) { - if(data.action == google.picker.Action.PICKED || data.action == google.picker.Action.CANCEL) { - if(data.action == google.picker.Action.PICKED) { - docs = data.docs; - } - hidePicker(); - task.chain(); - } - }); - picker = pickerBuilder.build(); - $(utils.createBackdrop()).click(function() { - hidePicker(); - task.chain(); - }); - picker.setVisible(true); - }); - task.onSuccess(function() { - callback(undefined, docs); - }); - task.onError(function(error) { - hidePicker(); - callback(error); - }); - task.enqueue(); - }; + googleHelper.rename = function(fileId, title, accountId, callback) { + var result; + var task = new AsyncTask(); + connect(task); + authenticate(task, 'gdrive', accountId); + task.onRun(function() { + var body = {'title': title}; + runWithToken(accountId, function() { + var request = gapi.client.drive.files.patch({ + 'fileId': fileId, + 'resource': body + }); + request.execute(function(response) { + if(response && response.id) { + // Rename success + result = response; + return task.chain(); + } + var error = response.error; + // Handle error + if(error !== undefined && fileId !== undefined) { + if(error.code === 404) { + error = 'File ID "' + fileId + '" not found on Google Drive.|removePublish'; + } + } + handleError(error, task); + }); + }); + }); + task.onSuccess(function() { + callback(undefined, result); + }); + task.onError(function(error) { + callback(error); + }); + task.enqueue(); + }; - googleHelper.uploadBlogger = function(blogUrl, blogId, postId, labelList, isDraft, publishDate, title, content, callback) { - var accountId = 'google.blogger0'; - var task = new AsyncTask(); - connect(task); - authenticate(task, 'blogger', accountId); - task.onRun(function() { - var headers = {}; - var authorizationMgr = authorizationMgrMap[accountId]; - if(authorizationMgr && authorizationMgr.token) { - headers.Authorization = "Bearer " + authorizationMgr.token.access_token; - } - function uploadPost() { - var url = "https://www.googleapis.com/blogger/v3/blogs/" + blogId + "/posts/"; - var data = { - kind: "blogger#post", - blog: { - id: blogId - }, - labels: labelList, - title: title, - content: content - }; - if(publishDate) { - data.published = publishDate.toISOString(); - } - var type = "POST"; - // If it's an update - if(postId !== undefined) { - url += postId; - data.id = postId; - type = "PUT"; - } - $.ajax({ - url: url, - data: JSON.stringify(data), - headers: headers, - type: type, - contentType: "application/json", - dataType: "json", - timeout: constants.AJAX_TIMEOUT - }).done(function(post) { - postId = post.id; - task.chain(publish); - }).fail(function(jqXHR) { - var error = { - code: jqXHR.status, - message: jqXHR.statusText - }; - // Handle error - if(error.code === 404 && postId !== undefined) { - error = 'Post ' + postId + ' not found on Blogger.|removePublish'; - } - handleError(error, task); - }); - } - function publish() { - var url = "https://www.googleapis.com/blogger/v3/blogs/" + blogId + "/posts/" + postId; - if(isDraft) { - url += "/revert"; - } - else { - url += "/publish"; - if(publishDate) { - url += '?publishDate=' + publishDate.toISOString(); - } - } - $.ajax({ - url: url, - headers: headers, - type: 'POST', - dataType: "json", - timeout: constants.AJAX_TIMEOUT - }).done(function() { - task.chain(); - }).fail(function(jqXHR) { - var error = { - code: jqXHR.status, - message: jqXHR.statusText - }; - // Handle error - if(error.code === 404) { - error = 'Post ' + postId + ' not found on Blogger.|removePublish'; - } - handleError(error, task); - }); - } - function getBlogId() { - if(blogId !== undefined) { - task.chain(uploadPost); - return; - } - $.ajax({ - url: "https://www.googleapis.com/blogger/v3/blogs/byurl", - data: { - url: blogUrl - }, - headers: headers, - dataType: "json", - timeout: constants.AJAX_TIMEOUT - }).done(function(blog) { - blogId = blog.id; - task.chain(uploadPost); - }).fail(function(jqXHR) { - var error = { - code: jqXHR.status, - message: jqXHR.statusText - }; - // Handle error - if(error.code === 404) { - error = 'Blog "' + blogUrl + '" not found on Blogger.|removePublish'; - } - handleError(error, task); - }); - } - task.chain(getBlogId); - }); - task.onSuccess(function() { - callback(undefined, blogId, postId); - }); - task.onError(function(error) { - callback(error); - }); - task.enqueue(); - }; + googleHelper.checkChanges = function(lastChangeId, accountId, callback) { + var changes = []; + var newChangeId = lastChangeId || 0; + var task = new AsyncTask(); + connect(task); + authenticate(task, 'gdrive', accountId); + task.onRun(function() { + var nextPageToken; - googleHelper.uploadBloggerPage = function(blogUrl, blogId, pageId, isDraft, publishDate, title, content, callback) { - var accountId = 'google.blogger0'; - var task = new AsyncTask(); - connect(task); - authenticate(task, 'blogger', accountId); - task.onRun(function() { - var headers = {}; - var authorizationMgr = authorizationMgrMap[accountId]; - if(authorizationMgr && authorizationMgr.token) { - headers.Authorization = "Bearer " + authorizationMgr.token.access_token; - } - function uploadPage() { - var url = "https://www.googleapis.com/blogger/v3/blogs/" + blogId + "/pages/"; - var data = { - kind: "blogger#page", - blog: { - id: blogId - }, - title: title, - content: content - }; - var type = "POST"; - // If it's an update - if(pageId !== undefined) { - url += pageId; - data.id = pageId; - type = "PUT"; - } - $.ajax({ - url: url, - data: JSON.stringify(data), - headers: headers, - type: type, - contentType: "application/json", - dataType: "json", - timeout: constants.AJAX_TIMEOUT - }).done(function(page) { - pageId = page.id; - task.chain(); - }).fail(function(jqXHR) { - var error = { - code: jqXHR.status, - message: jqXHR.statusText - }; - // Handle error - if(error.code === 404 && pageId !== undefined) { - error = 'Page ' + pageId + ' not found on Blogger.|removePublish'; - } - handleError(error, task); - }); - } - function getBlogId() { - if(blogId !== undefined) { - task.chain(uploadPage); - return; - } - $.ajax({ - url: "https://www.googleapis.com/blogger/v3/blogs/byurl", - data: { - url: blogUrl - }, - headers: headers, - dataType: "json", - timeout: constants.AJAX_TIMEOUT - }).done(function(blog) { - blogId = blog.id; - task.chain(uploadPage); - }).fail(function(jqXHR) { - var error = { - code: jqXHR.status, - message: jqXHR.statusText - }; - // Handle error - if(error.code === 404) { - error = 'Blog "' + blogUrl + '" not found on Blogger.|removePublish'; - } - handleError(error, task); - }); - } - task.chain(getBlogId); - }); - task.onSuccess(function() { - callback(undefined, blogId, pageId); - }); - task.onError(function(error) { - callback(error); - }); - task.enqueue(); - }; + function retrievePageOfChanges() { + runWithToken(accountId, function() { + var request; + if(nextPageToken === undefined) { + request = gapi.client.drive.changes.list({ + 'startChangeId': newChangeId + 1 + }); + } + else { + request = gapi.client.drive.changes.list({ + 'pageToken': nextPageToken + }); + } - // Use by Google's client.js - window.delayedFunction = undefined; - window.runDelayedFunction = function() { - if(window.delayedFunction !== undefined) { - window.delayedFunction(); - } - }; + request.execute(function(response) { + if(!response || !response.largestChangeId) { + // Handle error + return handleError(response.error, task); + } + // Retrieve success + newChangeId = response.largestChangeId; + nextPageToken = response.nextPageToken; + if(response.items !== undefined) { + changes = changes.concat(response.items); + } + if(nextPageToken !== undefined) { + task.chain(retrievePageOfChanges); + } + else { + task.chain(); + } + }); + }); + } - return googleHelper; + task.chain(retrievePageOfChanges); + }); + task.onSuccess(function() { + callback(undefined, changes, newChangeId); + }); + task.onError(function(error) { + callback(error); + }); + task.enqueue(); + }; + + googleHelper.downloadMetadata = function(ids, accountId, callback, skipAuth) { + var result = []; + var task = new AsyncTask(); + connect(task); + if(!skipAuth) { + authenticate(task, 'gdrive', accountId); + } + task.onRun(function() { + function recursiveDownloadMetadata() { + if(ids.length === 0) { + return task.chain(); + } + var id = ids[0]; + var headers = {}; + var authorizationMgr = authorizationMgrMap[accountId]; + if(authorizationMgr && authorizationMgr.token) { + headers.Authorization = "Bearer " + authorizationMgr.token.access_token; + } + $.ajax({ + url: "https://www.googleapis.com/drive/v2/files/" + id, + headers: headers, + data: { + key: constants.GOOGLE_API_KEY + }, + dataType: "json", + timeout: constants.AJAX_TIMEOUT + }).done(function(data) { + result.push(data); + ids.shift(); + task.chain(recursiveDownloadMetadata); + }).fail(function(jqXHR) { + var error = { + code: jqXHR.status, + message: jqXHR.statusText + }; + // Handle error + if(error.code === 404) { + error = 'File ID "' + id + '" not found on Google Drive.'; + } + handleError(error, task); + }); + } + + task.chain(recursiveDownloadMetadata); + }); + task.onSuccess(function() { + callback(undefined, result); + }); + task.onError(function(error) { + callback(error); + }); + task.enqueue(); + }; + + googleHelper.downloadContent = function(objects, accountId, callback, skipAuth) { + var result = []; + var task = new AsyncTask(); + // Add some time for user to choose his files + task.timeout = constants.ASYNC_TASK_LONG_TIMEOUT; + connect(task); + if(!skipAuth) { + authenticate(task, 'gdrive', accountId); + } + task.onRun(function() { + function recursiveDownloadContent() { + if(objects.length === 0) { + return task.chain(); + } + var object = objects[0]; + result.push(object); + var file; + // object may be a file + if(object.kind == "drive#file") { + file = object; + } + // object may be a change + else if(object.kind == "drive#change") { + file = object.file; + } + if(!file) { + objects.shift(); + return task.chain(recursiveDownloadContent); + } + var url = file.downloadUrl; + // if file is a real time document + if(file.mimeType.indexOf("application/vnd.google-apps.drive-sdk") === 0) { + file.isRealtime = true; + url = 'https://www.googleapis.com/drive/v2/files/' + file.id + '/realtime'; + } + var headers = {}; + var authorizationMgr = authorizationMgrMap[accountId]; + if(authorizationMgr && authorizationMgr.token) { + headers.Authorization = "Bearer " + authorizationMgr.token.access_token; + } + $.ajax({ + url: url, + headers: headers, + data: { + key: constants.GOOGLE_API_KEY + }, + dataType: file.isRealtime ? 'json' : 'text', + timeout: constants.AJAX_TIMEOUT + }).done(function(data) { + file.content = file.isRealtime ? data.data.value.content.value : data; + objects.shift(); + task.chain(recursiveDownloadContent); + }).fail(function(jqXHR) { + var error = { + code: jqXHR.status, + message: jqXHR.statusText + }; + // Handle error + handleError(error, task); + }); + } + + task.chain(recursiveDownloadContent); + }); + task.onSuccess(function() { + callback(undefined, result); + }); + task.onError(function(error) { + callback(error); + }); + task.enqueue(); + }; + + googleHelper.uploadImg = function(name, content, albumId, callback) { + var accountId = 'google.picasa0'; + var result; + var task = new AsyncTask(); + connect(task); + authenticate(task, 'picasa', accountId); + task.onRun(function() { + var headers = { + "Slug": name + }; + if(name.match(/.jpe?g$/i)) { + headers["Content-Type"] = "image/jpeg"; + } + else if(name.match(/.png$/i)) { + headers["Content-Type"] = "image/png"; + } + else if(name.match(/.gif$/i)) { + headers["Content-Type"] = "image/gif"; + } + var authorizationMgr = authorizationMgrMap[accountId]; + if(authorizationMgr && authorizationMgr.token) { + headers.Authorization = "Bearer " + authorizationMgr.token.access_token; + } + + $.ajax({ + url: constants.PICASA_PROXY_URL + "upload/" + albumId, + headers: headers, + data: content, + processData: false, + dataType: "xml", + timeout: constants.AJAX_TIMEOUT, + type: "POST" + }).done(function(data) { + result = data; + task.chain(); + }).fail(function(jqXHR) { + var error = { + code: jqXHR.status, + message: jqXHR.statusText + }; + if(error.code == 200) { + error.message = jqXHR.responseText; + } + handleError(error, task); + }); + }); + task.onSuccess(function() { + callback(undefined, result); + }); + task.onError(function(error) { + callback(error); + }); + task.enqueue(); + }; + + function handleError(error, task) { + var errorMsg; + if(error) { + logger.error(error); + // Try to analyze the error + if(typeof error === "string") { + errorMsg = error; + } + else { + errorMsg = "Google error (" + error.code + ": " + error.message + ")."; + if(error.code >= 500 && error.code < 600) { + // Retry as described in Google's best practices + return task.retry(new Error(errorMsg)); + } + else if(error.code === 401 || error.code === 403 || error.code == "token_refresh_required") { + _.each(authorizationMgrMap, function(authorizationMgr) { + authorizationMgr.setRefreshFlag(); + }); + errorMsg = "Access to Google account is not authorized."; + return task.retry(new Error(errorMsg), 1); + } + else if(error.code === 0 || error.code === -1) { + connected = false; + _.each(authorizationMgrMap, function(authorizationMgr) { + authorizationMgr.setRefreshFlag(); + }); + core.setOffline(); + errorMsg = "|stopPublish"; + } + } + } + task.error(new Error(errorMsg)); + } + + var pickerLoaded = false; + + function loadPicker(task) { + task.onRun(function() { + if(pickerLoaded === true) { + return task.chain(); + } + $.ajax({ + url: "//www.google.com/jsapi", + data: { + key: constants.GOOGLE_API_KEY + }, + dataType: "script", + timeout: constants.AJAX_TIMEOUT + }).done(function() { + google.load('picker', '1', { + callback: function() { + task.chain(); + } + }); + pickerLoaded = true; + }).fail(function(jqXHR) { + var error = { + code: jqXHR.status, + message: jqXHR.statusText + }; + handleError(error, task); + }); + }); + } + + googleHelper.picker = function(callback, pickerType, accountId) { + var docs = []; + var picker; + + function hidePicker() { + if(picker !== undefined) { + picker.setVisible(false); + $(".modal-backdrop, .picker").remove(); + } + } + + var task = new AsyncTask(); + // Add some time for user to choose his files + task.timeout = constants.ASYNC_TASK_LONG_TIMEOUT; + connect(task); + if(pickerType == 'doc' || pickerType == 'folder') { + authenticate(task, 'gdrive', accountId); + } + else { + accountId = 'google.picasa0'; + authenticate(task, 'picasa', accountId); + } + loadPicker(task); + task.onRun(function() { + var authorizationMgr = authorizationMgrMap[accountId]; + var pickerBuilder = new google.picker.PickerBuilder(); + pickerBuilder.setAppId(constants.GOOGLE_DRIVE_APP_ID); + var view; + if(pickerType == 'doc') { + view = new google.picker.DocsView(google.picker.ViewId.DOCS); + view.setParent('root'); + view.setIncludeFolders(true); + view.setMimeTypes([ + "text/x-markdown", + "text/plain", + "application/octet-stream", + "application/vnd.google-apps.drive-sdk." + constants.GOOGLE_DRIVE_APP_ID + ].join(",")); + pickerBuilder.enableFeature(google.picker.Feature.NAV_HIDDEN); + pickerBuilder.enableFeature(google.picker.Feature.MULTISELECT_ENABLED); + pickerBuilder.addView(view); + authorizationMgr && authorizationMgr.token && pickerBuilder.setOAuthToken(authorizationMgr.token.access_token); + } + else if(pickerType == 'folder') { + view = new google.picker.DocsView(google.picker.ViewId.FOLDERS); + view.setParent('root'); + view.setIncludeFolders(true); + view.setSelectFolderEnabled(true); + view.setMimeTypes('application/vnd.google-apps.folder'); + pickerBuilder.enableFeature(google.picker.Feature.NAV_HIDDEN); + pickerBuilder.addView(view); + authorizationMgr && authorizationMgr.token && pickerBuilder.setOAuthToken(authorizationMgr.token.access_token); + } + else if(pickerType == 'img') { + view = new google.picker.PhotosView(); + view.setType('flat'); + pickerBuilder.addView(view); + view = new google.picker.PhotosView(); + view.setType('ofuser'); + pickerBuilder.addView(view); + pickerBuilder.addView(google.picker.ViewId.PHOTO_UPLOAD); + authorizationMgr && authorizationMgr.token && pickerBuilder.setOAuthToken(authorizationMgr.token.access_token); + } + pickerBuilder.setCallback(function(data) { + if(data.action == google.picker.Action.PICKED || data.action == google.picker.Action.CANCEL) { + if(data.action == google.picker.Action.PICKED) { + docs = data.docs; + } + hidePicker(); + task.chain(); + } + }); + picker = pickerBuilder.build(); + var $backdrop = $(utils.createBackdrop()).on('click.backdrop', function() { + hidePicker(); + task.chain(); + }); + picker.setVisible(true); + }); + task.onSuccess(function() { + callback(undefined, docs); + }); + task.onError(function(error) { + hidePicker(); + callback(error); + }); + task.enqueue(); + }; + + googleHelper.uploadBlogger = function(blogUrl, blogId, postId, labelList, isDraft, publishDate, title, content, callback) { + var accountId = 'google.blogger0'; + var task = new AsyncTask(); + connect(task); + authenticate(task, 'blogger', accountId); + task.onRun(function() { + var headers = {}; + var authorizationMgr = authorizationMgrMap[accountId]; + if(authorizationMgr && authorizationMgr.token) { + headers.Authorization = "Bearer " + authorizationMgr.token.access_token; + } + function uploadPost() { + var url = "https://www.googleapis.com/blogger/v3/blogs/" + blogId + "/posts/"; + var data = { + kind: "blogger#post", + blog: { + id: blogId + }, + labels: labelList, + title: title, + content: content + }; + if(publishDate) { + data.published = publishDate.toISOString(); + } + var type = "POST"; + // If it's an update + if(postId !== undefined) { + url += postId; + data.id = postId; + type = "PUT"; + } + $.ajax({ + url: url, + data: JSON.stringify(data), + headers: headers, + type: type, + contentType: "application/json", + dataType: "json", + timeout: constants.AJAX_TIMEOUT + }).done(function(post) { + postId = post.id; + task.chain(publish); + }).fail(function(jqXHR) { + var error = { + code: jqXHR.status, + message: jqXHR.statusText + }; + // Handle error + if(error.code === 404 && postId !== undefined) { + error = 'Post ' + postId + ' not found on Blogger.|removePublish'; + } + handleError(error, task); + }); + } + + function publish() { + var url = "https://www.googleapis.com/blogger/v3/blogs/" + blogId + "/posts/" + postId; + if(isDraft) { + url += "/revert"; + } + else { + url += "/publish"; + if(publishDate) { + url += '?publishDate=' + publishDate.toISOString(); + } + } + $.ajax({ + url: url, + headers: headers, + type: 'POST', + dataType: "json", + timeout: constants.AJAX_TIMEOUT + }).done(function() { + task.chain(); + }).fail(function(jqXHR) { + var error = { + code: jqXHR.status, + message: jqXHR.statusText + }; + // Handle error + if(error.code === 404) { + error = 'Post ' + postId + ' not found on Blogger.|removePublish'; + } + handleError(error, task); + }); + } + + function getBlogId() { + if(blogId !== undefined) { + task.chain(uploadPost); + return; + } + $.ajax({ + url: "https://www.googleapis.com/blogger/v3/blogs/byurl", + data: { + url: blogUrl + }, + headers: headers, + dataType: "json", + timeout: constants.AJAX_TIMEOUT + }).done(function(blog) { + blogId = blog.id; + task.chain(uploadPost); + }).fail(function(jqXHR) { + var error = { + code: jqXHR.status, + message: jqXHR.statusText + }; + // Handle error + if(error.code === 404) { + error = 'Blog "' + blogUrl + '" not found on Blogger.|removePublish'; + } + handleError(error, task); + }); + } + + task.chain(getBlogId); + }); + task.onSuccess(function() { + callback(undefined, blogId, postId); + }); + task.onError(function(error) { + callback(error); + }); + task.enqueue(); + }; + + googleHelper.uploadBloggerPage = function(blogUrl, blogId, pageId, isDraft, publishDate, title, content, callback) { + var accountId = 'google.blogger0'; + var task = new AsyncTask(); + connect(task); + authenticate(task, 'blogger', accountId); + task.onRun(function() { + var headers = {}; + var authorizationMgr = authorizationMgrMap[accountId]; + if(authorizationMgr && authorizationMgr.token) { + headers.Authorization = "Bearer " + authorizationMgr.token.access_token; + } + function uploadPage() { + var url = "https://www.googleapis.com/blogger/v3/blogs/" + blogId + "/pages/"; + var data = { + kind: "blogger#page", + blog: { + id: blogId + }, + title: title, + content: content + }; + var type = "POST"; + // If it's an update + if(pageId !== undefined) { + url += pageId; + data.id = pageId; + type = "PUT"; + } + $.ajax({ + url: url, + data: JSON.stringify(data), + headers: headers, + type: type, + contentType: "application/json", + dataType: "json", + timeout: constants.AJAX_TIMEOUT + }).done(function(page) { + pageId = page.id; + task.chain(); + }).fail(function(jqXHR) { + var error = { + code: jqXHR.status, + message: jqXHR.statusText + }; + // Handle error + if(error.code === 404 && pageId !== undefined) { + error = 'Page ' + pageId + ' not found on Blogger.|removePublish'; + } + handleError(error, task); + }); + } + + function getBlogId() { + if(blogId !== undefined) { + task.chain(uploadPage); + return; + } + $.ajax({ + url: "https://www.googleapis.com/blogger/v3/blogs/byurl", + data: { + url: blogUrl + }, + headers: headers, + dataType: "json", + timeout: constants.AJAX_TIMEOUT + }).done(function(blog) { + blogId = blog.id; + task.chain(uploadPage); + }).fail(function(jqXHR) { + var error = { + code: jqXHR.status, + message: jqXHR.statusText + }; + // Handle error + if(error.code === 404) { + error = 'Blog "' + blogUrl + '" not found on Blogger.|removePublish'; + } + handleError(error, task); + }); + } + + task.chain(getBlogId); + }); + task.onSuccess(function() { + callback(undefined, blogId, pageId); + }); + task.onError(function(error) { + callback(error); + }); + task.enqueue(); + }; + + // Use by Google's client.js + window.delayedFunction = undefined; + window.runDelayedFunction = function() { + if(window.delayedFunction !== undefined) { + window.delayedFunction(); + } + }; + + return googleHelper; }); diff --git a/public/res/helpers/teamserverHelper.js b/public/res/helpers/teamserverHelper.js index 8156f997..cc9160f0 100644 --- a/public/res/helpers/teamserverHelper.js +++ b/public/res/helpers/teamserverHelper.js @@ -6,10 +6,9 @@ define([ "utils", "storage", "logger", - "settings", "eventMgr", "classes/AsyncTask" -], function(_, $, constants, core, utils, storage, logger, settings, eventMgr, AsyncTask) { +], function(_, $, constants, core, utils, storage, logger, eventMgr, AsyncTask) { var connected = false; var authenticated = true; @@ -35,8 +34,8 @@ define([ url: constants.TEAM_SERVER_URL + 'ping', timeout: constants.AJAX_TIMEOUT }).done(function() { - task.chain(); - }).fail(function(jqXHR) { + task.chain(); + }).fail(function(jqXHR) { var error = { code: jqXHR.status, message: jqXHR.statusText @@ -60,7 +59,7 @@ define([ connect(task); authenticate(task); task.onRun(function() { - var url = settings.teamserverURL + '/repo/' + repo + '/document'; + var url = constants.TEAM_SERVER_URL + 'repo/' + repo + '/document'; var type = 'POST'; if(id) { url += '/' + id; @@ -77,7 +76,7 @@ define([ timeout: constants.AJAX_TIMEOUT }).done(function(data) { result = data; - task.chain(); + task.chain(); }).fail(function(jqXHR) { var error = { code: jqXHR.status, @@ -99,14 +98,14 @@ define([ task.enqueue(); }; - teamserverHelper.checkChanges = function(repo, lastChangeId, accountId, callback) { + teamserverHelper.checkChanges = function(repo, lastChangeId, callback) { var changes; var newChangeId = lastChangeId; var task = new AsyncTask(); connect(task); authenticate(task); task.onRun(function() { - var url = settings.teamserverURL + '/repo/' + repo + '/changes/'; + var url = constants.TEAM_SERVER_URL + 'repo/' + repo + '/changes/'; var type = 'GET'; if(lastChangeId) { url += lastChangeId; @@ -119,7 +118,7 @@ define([ }).done(function(data) { newChangeId = data.newChangeId; changes = data.changes; - task.chain(); + task.chain(); }).fail(function(jqXHR) { var error = { code: jqXHR.status, @@ -148,7 +147,7 @@ define([ return task.chain(); } var id = ids[0]; - var url = settings.teamserverURL + '/repo/' + repo + '/document/' + id; + var url = constants.TEAM_SERVER_URL + 'repo/' + repo + '/document/' + id; $.ajax({ url: url, dataType: "json", @@ -168,6 +167,7 @@ define([ handleError(error, task); }); } + task.chain(recursiveDownloadMetadata); }); task.onSuccess(function() { @@ -206,14 +206,16 @@ define([ task.error(new Error(errorMsg)); } + var $windowElt = $(window); + var origin = window.location.protocol + '//' + window.location.host; teamserverHelper.picker = function(repo, callback) { var docs = []; - var picker; + var iframe; function hidePicker() { - if(picker !== undefined) { - picker.setVisible(false); - $(".modal-backdrop, .picker").remove(); + if(iframe !== undefined) { + iframe.removeIframe(); + $windowElt.off('message.teamserver'); } } @@ -221,10 +223,17 @@ define([ // Add some time for user to choose his files task.timeout = constants.ASYNC_TASK_LONG_TIMEOUT; connect(task); - task.onRun(function() { - utils.iframe(constants.TEAM_SERVER_URL + 'index.html#/documentPicker', 800, 600); - task.chain(); - }); + task.onRun(function() { + iframe = utils.iframe(constants.TEAM_SERVER_URL + 'teamserver.html#/documentPicker', 550, 500); + $windowElt.on('message.teamserver', function(evt) { + evt = evt.originalEvent; + if(evt.origin == origin && evt.data.status == 'DocumentPickerFinished') { + docs = evt.data.documents; + hidePicker(); + task.chain(); + } + }); + }); task.onSuccess(function() { callback(undefined, docs); }); diff --git a/public/res/layout.js b/public/res/layout.js index d4398acb..a3fdb38a 100644 --- a/public/res/layout.js +++ b/public/res/layout.js @@ -116,7 +116,7 @@ define([ }; DomObject.prototype.createToggler = function(backdrop) { - var backdropElt; + var $backdropElt; var pushedEvents = 0; this.toggle = function(show) { if(show === this.isOpen) { @@ -126,7 +126,7 @@ define([ if(this.isOpen) { this.$elt.addClass('panel-open').trigger('show.layout.toggle'); if(backdrop) { - $(backdropElt = utils.createBackdrop(wrapperL1.elt)).click(_.bind(function() { + $backdropElt = $(utils.createBackdrop(wrapperL1.elt)).on('click.backdrop', _.bind(function() { this.toggle(false); }, this)); this.$elt.addClass('bring-to-front'); @@ -139,8 +139,11 @@ define([ } else { this.$elt.trigger('hide.layout.toggle'); - backdropElt && backdropElt.removeBackdrop(); - backdropElt = undefined; + if($backdropElt) { + $backdropElt.off('click.backdrop'); + $backdropElt[0].removeBackdrop(); + $backdropElt = undefined; + } transitionEndCallbacks.push(_.bind(function() { if(--pushedEvents === 0) { !this.isOpen && this.$elt.removeClass('panel-open bring-to-front').trigger('hidden.layout.toggle'); diff --git a/public/res/providers/teamserverProviderBuilder.js b/public/res/providers/teamserverProviderBuilder.js index e61dbab0..6c6d0554 100644 --- a/public/res/providers/teamserverProviderBuilder.js +++ b/public/res/providers/teamserverProviderBuilder.js @@ -14,7 +14,7 @@ define([ ], function($, _, constants, utils, storage, logger, Provider, settings, eventMgr, fileMgr, editor, teamserverHelper) { return function(providerId, providerName) { - var repo = 'teamserver'; + var repo = 'test'; var teamserverProvider = new Provider(providerId, providerName); @@ -131,8 +131,8 @@ define([ return callback(error); } var interestingChanges = []; - _.each(changes, function(change) { - var syncIndex = createSyncIndex(change.id); + _.each(changes, function(change, id) { + var syncIndex = createSyncIndex(id); var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex); var syncAttributes = fileDesc && fileDesc.syncLocations[syncIndex]; if(!syncAttributes) { @@ -151,7 +151,7 @@ define([ interestingChanges.push(change); } }); - teamserverHelper.downloadContent(repo, interestingChanges, function(error, changes) { + teamserverHelper.download(repo, interestingChanges, function(error, changes) { if(error) { return callback(error); } diff --git a/public/res/styles/main.less b/public/res/styles/main.less index 827fa09e..307e1fd3 100644 --- a/public/res/styles/main.less +++ b/public/res/styles/main.less @@ -192,18 +192,27 @@ body { .translate(0, 0); } - .modal-content { - background-color: @secondary-bg-light; - } +} - .modal-body { - background-color: @secondary-bg-lighter; - padding-bottom: 30px; - } +.modal-content { + background-color: @secondary-bg-light; +} - .modal-footer { - margin-top: 0; - } +.modal-body { + background-color: @secondary-bg-lighter; + padding-bottom: 30px; +} + +.modal-footer { + background-color: @secondary-bg-light; + margin-top: 0; +} + +.modal-iframe { + display: block; + margin: 30px auto 0; + z-index: 1040; + border-radius: 0; } a { diff --git a/public/res/utils.js b/public/res/utils.js index 1db5a343..7f360a51 100644 --- a/public/res/utils.js +++ b/public/res/utils.js @@ -308,21 +308,35 @@ define([ ].join("")); }; - utils.iframe = function(url, width, height, css) { - $("