11.3.2 release

This commit is contained in:
David Benson [draw.io] 2019-09-27 11:51:03 +01:00
parent 86d0f98d4b
commit 55312c1895
68 changed files with 897 additions and 1116 deletions

View file

@ -1,3 +1,5 @@
- Fixes default grid color in dark mode
26-SEP-2019: 11.3.2
- Updates Dell rack server stencils

View file

@ -11,6 +11,7 @@ import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
@ -20,7 +21,9 @@ import javax.servlet.http.HttpServletResponse;
@SuppressWarnings("serial")
abstract public class AbsAuthServlet extends HttpServlet
{
private static final boolean DEBUG = false;
private static final String SEPARATOR = "/:::/";
static public class Config
{
public String DEV_CLIENT_SECRET = null, CLIENT_SECRET = null, DEV_CLIENT_ID = null, CLIENT_ID = null,
@ -32,6 +35,13 @@ abstract public class AbsAuthServlet extends HttpServlet
return null;
}
protected String processAuthError(String errorCode)
{
//Usually sending null is enough as it is used as a value for auth info
//If more processing is needed, override this method
return processAuthResponse("null", false);
}
protected String processAuthResponse(String authRes, boolean jsonResponse)
{
return "";
@ -45,23 +55,88 @@ abstract public class AbsAuthServlet extends HttpServlet
{
String code = request.getParameter("code");
String refreshToken = request.getParameter("refresh_token");
String error = request.getParameter("error");
HashMap<String, String> stateVars = new HashMap<>();
try
{
String state = request.getParameter("state");
if (state != null)
{
String[] parts = state.split("&");
for (String part : parts)
{
String[] keyVal = part.split("=");
stateVars.put(keyVal[0], keyVal[1]);
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
int configIndex = 0;
try
{
String appIndex = stateVars.get("appIndex");
if (appIndex != null)
{
configIndex = Integer.parseInt(appIndex);
}
}
catch (Exception e)
{
e.printStackTrace();
}
Config CONFIG = getConfig();
String secret, client, redirectUri;
String[] secrets, clients;
if ("127.0.0.1".equals(request.getServerName()))
{
secret = CONFIG.DEV_CLIENT_SECRET;
client = CONFIG.DEV_CLIENT_ID;
secrets = CONFIG.DEV_CLIENT_SECRET.split(SEPARATOR);
clients = CONFIG.DEV_CLIENT_ID.split(SEPARATOR);
redirectUri = CONFIG.DEV_REDIRECT_URI;
}
else
{
secret = CONFIG.CLIENT_SECRET;
client = CONFIG.CLIENT_ID;
secrets = CONFIG.CLIENT_SECRET.split(SEPARATOR);
clients = CONFIG.CLIENT_ID.split(SEPARATOR);
redirectUri = CONFIG.REDIRECT_URI;
}
if (code == null && refreshToken == null)
secret = secrets.length > configIndex ? secrets[configIndex] : secrets[0];
client = clients.length > configIndex ? clients[configIndex] : clients[0];
if (error != null)
{
try
{
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
OutputStream out = response.getOutputStream();
PrintWriter writer = new PrintWriter(out);
// Writes JavaScript code
writer.println(processAuthError(error));
writer.flush();
writer.close();
}
catch(Exception e)
{
e.printStackTrace();
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
}
else if (code == null && refreshToken == null)
{
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
}
@ -135,7 +210,8 @@ abstract public class AbsAuthServlet extends HttpServlet
catch(IOException e)
{
e.printStackTrace();
StringBuilder details = new StringBuilder("");
if (con != null)
{
try
@ -148,6 +224,8 @@ abstract public class AbsAuthServlet extends HttpServlet
while ((inputLine = in.readLine()) != null)
{
System.err.println(inputLine);
details.append(inputLine);
details.append("\n");
}
in.close();
}
@ -165,6 +243,19 @@ abstract public class AbsAuthServlet extends HttpServlet
{
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
if (DEBUG)
{
OutputStream out = response.getOutputStream();
PrintWriter writer = new PrintWriter(out);
e.printStackTrace(writer);
writer.println(details.toString());
writer.flush();
writer.close();
}
}
catch (Exception e)
{

View file

@ -279,12 +279,7 @@ App.PUSHER_URL = 'https://js.pusher.com/4.3/pusher.min.js';
* Google APIs to load. The realtime API is needed to notify collaborators of conversion
* of the realtime files, but after Dec 11 it's read-only and hence no longer needed.
*/
App.GOOGLE_APIS = 'client,drive-share';
/**
* Google Realtime API export endpoint end of life on 09/27/2019.
*/
App.GOOGLE_REALTIME_EOL = 1569535200000;
App.GOOGLE_APIS = 'drive-share';
/**
* Function: authorize
@ -910,7 +905,7 @@ mxUtils.extend(App, EditorUi);
/**
* Executes the first step for connecting to Google Drive.
*/
App.prototype.defaultUserPicture = 'https://lh3.googleusercontent.com/-HIzvXUy6QUY/AAAAAAAAAAI/AAAAAAAAAAA/giuR7PQyjEk/photo.jpg?sz=30';
App.prototype.defaultUserPicture = 'https://lh3.googleusercontent.com/-HIzvXUy6QUY/AAAAAAAAAAI/AAAAAAAAAAA/giuR7PQyjEk/photo.jpg?sz=64';
/**
*
@ -1262,42 +1257,6 @@ App.prototype.init = function()
this.updateUserElement();
this.restoreLibraries();
this.checkLicense();
// Stop notification one day before API is disabled
if (App.GOOGLE_REALTIME_EOL - Date.now() > 86400000)
{
if (this.drive.user != null && (!isLocalStorage || mxSettings.settings == null ||
mxSettings.settings.closeRealtimeWarning == null || mxSettings.settings.closeRealtimeWarning <
new Date().getTime() - (2 * 24 * 60 * 60 * 1000)) &&
(!this.editor.chromeless || this.editor.editable))
{
this.drive.checkRealtimeFiles(mxUtils.bind(this, function()
{
var footer = createFooter('You need to take action to convert legacy files. Click here.',
'https://desk.draw.io/support/solutions/articles/16000092210',
'geStatusAlert',
mxUtils.bind(this, function()
{
footer.parentNode.removeChild(footer);
this.hideFooter();
// Close permanently
if (isLocalStorage && mxSettings.settings != null)
{
mxSettings.settings.closeRealtimeWarning = Date.now();
mxSettings.save();
}
}));
document.body.appendChild(footer);
window.setTimeout(mxUtils.bind(this, function()
{
mxUtils.setPrefixedStyle(footer.style, 'transform', 'translate(-50%,0%)');
}), 1500);
}));
}
}
}))
// Notifies listeners of new client
@ -1306,24 +1265,7 @@ App.prototype.init = function()
if (window.DrawGapiClientCallback != null)
{
gapi.load(((urlParams['picker'] != '0') ? 'picker,': '') + 'auth:' + App.GOOGLE_APIS, mxUtils.bind(this, function(resp)
{
// Starts the app without the Google Option if the API fails to load
if (gapi.client != null)
{
gapi.client.load('drive', 'v2', mxUtils.bind(this, function()
{
// Needed to avoid popup blocking for non-immediate authentication
gapi.auth.init(mxUtils.bind(this, function()
{
if (gapi.client.drive != null)
{
doInit();
}
}));
}));
}
}));
gapi.load(((urlParams['picker'] != '0') ? 'picker,': '') + App.GOOGLE_APIS, doInit);
/**
* Clears any callbacks.
@ -2436,33 +2378,7 @@ App.prototype.loadGapi = function(then)
{
if (typeof gapi !== 'undefined')
{
gapi.load(((urlParams['picker'] != '0') ? 'picker,': '') + 'auth:' + App.GOOGLE_APIS, mxUtils.bind(this, function(resp)
{
// Starts the app without the Google Option if the API fails to load
if (gapi.client == null)
{
this.drive = null;
this.mode = null;
then();
}
else
{
gapi.client.load('drive', 'v2', mxUtils.bind(this, function()
{
// Needed to avoid popup blocking for non-immediate authentication
gapi.auth.init(mxUtils.bind(this, function()
{
if (gapi.client.drive == null)
{
this.drive = null;
this.mode = null;
}
then();
}));
}));
}
}));
gapi.load(((urlParams['picker'] != '0') ? 'picker,': '') + App.GOOGLE_APIS, then);
}
};
@ -5823,75 +5739,140 @@ App.prototype.updateUserElement = function()
if (this.drive != null)
{
var driveUser = this.drive.getUser();
var driveUsers = this.drive.getUsersList();
if (driveUser != null)
if (driveUsers.length > 0)
{
connected = true;
this.userPanel.innerHTML += '<table title="User ID: ' + driveUser.id +
'" style="font-size:10pt;padding:20px 20px 10px 10px;">' +
'<tr><td valign="middle">' +
((driveUser.pictureUrl != null) ?
'<img width="50" height="50" style="margin-right:8px;border-radius:50%;" src="' + driveUser.pictureUrl + '"/>' :
'<img width="46" height="46" style="margin-right:4px;margin-top:0px;" src="' + this.defaultUserPicture + '"/>') +
'</td><td valign="top" style="white-space:nowrap;' +
((driveUser.pictureUrl != null) ? 'padding-top:4px;' : '') +
'">' + mxUtils.htmlEntities(driveUser.displayName) + '<br>' +
'<small style="color:gray;">' + mxUtils.htmlEntities(driveUser.email) +
'</small><div style="margin-top:4px;"><i>' +
mxResources.get('googleDrive') + '</i></div></tr></table>';
var div = document.createElement('div');
div.style.textAlign = 'center';
div.style.paddingBottom = '12px';
div.style.whiteSpace = 'nowrap';
// LATER: Cannot change user while file is open since close will not work with new
// credentials and closing the file using fileLoaded(null) will show splash dialog.
var btn = mxUtils.button(mxResources.get('signOut'), mxUtils.bind(this, function()
var closeFile = mxUtils.bind(this, function(callback, spinnerMsg)
{
var file = this.getCurrentFile();
if (file != null && file.constructor == DriveFile)
{
this.confirm(mxResources.get('areYouSure'), mxUtils.bind(this, function()
this.spinner.spin(document.body, spinnerMsg);
// file.close();
this.fileLoaded(null);
// LATER: Use callback to wait for thumbnail update
window.setTimeout(mxUtils.bind(this, function()
{
this.spinner.spin(document.body, mxResources.get('signOut'));
this.diagramContainer.style.display = 'none';
this.formatContainer.style.display = 'none';
this.hsplit.style.display = 'none';
this.sidebarContainer.style.display = 'none';
this.sidebarFooterContainer.style.display = 'none';
if (this.tabContainer != null)
{
this.tabContainer.style.display = 'none';
}
file.close();
// LATER: Use callback to wait for thumbnail update
window.setTimeout(mxUtils.bind(this, function()
{
// Workaround to disable the splash screen before reload
this.showDialog = function() {};
window.location.hash = '';
this.drive.clearUserId();
gapi.auth.signOut();
// Reload page to reset client auth
window.location.reload();
}), (file != null && file.constructor == DriveFile) ? 2000 : 0);
}));
this.spinner.stop();
callback();
}), 2000);
}
else
{
this.drive.clearUserId();
this.drive.setUser(null);
gapi.auth.signOut();
callback();
}
});
var createUserRow = mxUtils.bind(this, function (user)
{
var tr = document.createElement('tr');
tr.style.cssText = user.isCurrent? '' : 'background-color: whitesmoke; cursor: pointer';
tr.setAttribute('title', 'User ID: ' + user.id);
tr.innerHTML = '<td valign="middle" style="height: 59px;width: 66px;' +
(user.isCurrent? '' : 'border-top: 1px solid rgb(224, 224, 224);') + '">' +
'<img width="50" height="50" style="margin: 4px 8px 0 8px;border-radius:50%;" src="' +
((user.pictureUrl != null) ? user.pictureUrl : this.defaultUserPicture) + '"/>' +
'</td><td valign="middle" style="white-space:nowrap;' +
((user.pictureUrl != null) ? 'padding-top:4px;' : '') +
(user.isCurrent? '' : 'border-top: 1px solid rgb(224, 224, 224);') +
'">' + mxUtils.htmlEntities(user.displayName) + '<br>' +
'<small style="color:gray;">' + mxUtils.htmlEntities(user.email) +
'</small><div style="margin-top:4px;"><i>' +
mxResources.get('googleDrive') + '</i></div>';
if (!user.isCurrent)
{
mxEvent.addListener(tr, 'click', mxUtils.bind(this, function(evt)
{
closeFile(mxUtils.bind(this, function()
{
this.stateArg = null;
this.drive.setUser(user);
this.drive.authorize(true, mxUtils.bind(this, function()
{
this.setMode(App.MODE_GOOGLE);
this.hideDialog();
this.showSplash();
}), mxUtils.bind(this, function(resp)
{
this.handleError(resp);
}), true); //Remember is true since add account imply keeping that account
}), mxResources.get('closingFile', null, 'Closing file...'));
mxEvent.consume(evt);
}));
}
return tr;
});
connected = true;
var driveUserTable = document.createElement('table');
driveUserTable.style.cssText ='font-size:10pt;padding: 20px 0 0 0;min-width: 300px;border-spacing: 0;';
for (var i = 0; i < driveUsers.length; i++)
{
driveUserTable.appendChild(createUserRow(driveUsers[i]));
}
this.userPanel.appendChild(driveUserTable);
var div = document.createElement('div');
div.style.textAlign = 'left';
div.style.padding = '8px';
div.style.whiteSpace = 'nowrap';
div.style.borderTop = '1px solid rgb(224, 224, 224)';
var btn = mxUtils.button(mxResources.get('signOut'), mxUtils.bind(this, function()
{
this.confirm(mxResources.get('areYouSure'), mxUtils.bind(this, function()
{
closeFile(mxUtils.bind(this, function()
{
this.stateArg = null;
this.drive.logout();
this.setMode(App.MODE_GOOGLE);
this.hideDialog();
this.showSplash();
}), mxResources.get('signOut'));
}));
}));
btn.className = 'geBtn';
btn.style.float = 'right';
div.appendChild(btn);
var btn = mxUtils.button(mxResources.get('addAccount', null, 'Add Account'), mxUtils.bind(this, function()
{
var authWin = this.drive.createAuthWin();
//FIXME This doean't work to set focus back to main window until closing the file is done
authWin.blur();
window.focus();
closeFile(mxUtils.bind(this, function()
{
this.stateArg = null;
this.drive.authorize(false, mxUtils.bind(this, function()
{
this.setMode(App.MODE_GOOGLE);
this.hideDialog();
this.showSplash();
}), mxUtils.bind(this, function(resp)
{
this.handleError(resp);
}), true, authWin); //Remember is true since add account imply keeping that account
}), mxResources.get('closingFile', null, 'Closing file...'));
}));
btn.className = 'geBtn';
btn.style.margin = '0px';
div.appendChild(btn);
this.userPanel.appendChild(div);
}
@ -5907,12 +5888,17 @@ App.prototype.updateUserElement = function()
}
connected = true;
this.userPanel.innerHTML += '<table style="font-size:10pt;padding:20px 20px 10px 10px;"><tr><td valign="top">' +
var userTable = document.createElement('table');
userTable.style.cssText = 'font-size:10pt;padding:' + (connected? '10' : '20') + 'px 20px 10px 10px;';
userTable.innerHTML += '<tr><td valign="top">' +
((logo != null) ? '<img style="margin-right:6px;" src="' + logo + '" width="40" height="40"/></td>' : '') +
'<td valign="middle" style="white-space:nowrap;">' + mxUtils.htmlEntities(user.displayName) +
((user.email != null) ? '<br><small style="color:gray;">' + mxUtils.htmlEntities(user.email) + '</small>' : '') +
((label != null) ? '<div style="margin-top:4px;"><i>' + mxUtils.htmlEntities(label) + '</i></div>' : '') +
'</td></tr></table>';
'</td></tr>';
this.userPanel.appendChild(userTable);
var div = document.createElement('div');
div.style.textAlign = 'center';
div.style.paddingBottom = '12px';

View file

@ -736,7 +736,7 @@ var SplashDialog = function(editorUi)
if (!mxClient.IS_CHROMEAPP && !EditorUi.isElectronApp)
{
var driveUser = (editorUi.drive != null) ? editorUi.drive.getUser() : null;
var driveUsers = (editorUi.drive != null) ? editorUi.drive.getUsersList() : [];
function addLogout(logout)
{
@ -763,48 +763,68 @@ var SplashDialog = function(editorUi)
buttons.appendChild(link);
};
if (editorUi.mode == App.MODE_GOOGLE && driveUser != null)
if (editorUi.mode == App.MODE_GOOGLE && driveUsers.length > 0)
{
btn.style.marginBottom = '24px';
var link = document.createElement('a');
link.setAttribute('href', 'javascript:void(0)');
link.style.display = 'inline-block';
link.style.marginTop = '6px';
mxUtils.write(link, mxResources.get('changeUser') + ' (' + driveUser.displayName + ')');
var title = document.createElement('span');
title.style.marginTop = '6px';
mxUtils.write(title, mxResources.get('changeUser') + ':');
// Makes room after last big buttons
btn.style.marginBottom = '16px';
buttons.style.paddingBottom = '18px';
mxEvent.addListener(link, 'click', function()
buttons.appendChild(title);
var usersSelect = document.createElement('select');
usersSelect.style.marginLeft = '4px';
usersSelect.style.width = '200px';
for (var i = 0; i < driveUsers.length; i++)
{
editorUi.hideDialog();
editorUi.drive.clearUserId();
editorUi.drive.setUser(null);
gapi.auth.signOut();
// Restores current dialog after clearing user
editorUi.setMode(App.MODE_GOOGLE);
editorUi.hideDialog();
editorUi.showSplash();
var option = document.createElement('option');
mxUtils.write(option, driveUsers[i].displayName);
option.value = i;
usersSelect.appendChild(option);
//More info (email) about the user in a disabled option
option = document.createElement('option');
option.innerHTML = '&nbsp;&nbsp;&nbsp;';
mxUtils.write(option, '<' + driveUsers[i].email + '>');
option.setAttribute('disabled', 'disabled');
usersSelect.appendChild(option);
}
//Add account option
var option = document.createElement('option');
mxUtils.write(option, mxResources.get('addAccount'));
option.value = driveUsers.length;
usersSelect.appendChild(option);
mxEvent.addListener(usersSelect, 'change', function()
{
var userIndex = usersSelect.value;
var existingAccount = driveUsers.length != userIndex;
// FIXME: Does not force showing the auth dialog if only one user is logged in
editorUi.drive.authorize(false, mxUtils.bind(this, mxUtils.bind(this, function()
if (existingAccount)
{
editorUi.drive.setUser(driveUsers[userIndex]);
}
editorUi.drive.authorize(existingAccount, function()
{
editorUi.setMode(App.MODE_GOOGLE);
editorUi.hideDialog();
editorUi.showSplash();
})), mxUtils.bind(this, function(resp)
}, function(resp)
{
editorUi.handleError(resp, null, function()
{
editorUi.hideDialog();
editorUi.showSplash();
});
}));
}, true);
});
buttons.appendChild(link);
buttons.appendChild(usersSelect);
}
else if (editorUi.mode == App.MODE_ONEDRIVE && editorUi.oneDrive != null)
{
@ -1980,11 +2000,10 @@ var BackgroundImageDialog = function(editorUi, applyFn)
// Creates one picker and reuses it to avoid polluting the DOM
if (editorUi.photoPicker == null)
{
var token = gapi.auth.getToken().access_token;
var picker = new google.picker.PickerBuilder()
.setAppId(editorUi.drive.appId)
.setLocale(mxLanguage)
.setOAuthToken(token)
.setOAuthToken(editorUi.drive.token)
.addView(google.picker.ViewId.PHOTO_UPLOAD);
editorUi.photoPicker = picker.setCallback(function(data)
@ -4541,11 +4560,10 @@ var ImageDialog = function(editorUi, title, initialValue, fn, ignoreExisting, co
// Creates one picker and reuses it to avoid polluting the DOM
if (editorUi.photoPicker == null)
{
var token = gapi.auth.getToken().access_token;
var picker = new google.picker.PickerBuilder()
.setAppId(editorUi.drive.appId)
.setLocale(mxLanguage)
.setOAuthToken(token)
.setOAuthToken(editorUi.drive.token)
.addView(google.picker.ViewId.PHOTO_UPLOAD);
editorUi.photoPicker = picker.setCallback(function(data)
@ -4931,7 +4949,6 @@ var LinkDialog = function(editorUi, initialValue, btnLabel, fn, showPages)
// Creates one picker and reuses it to avoid polluting the DOM
if (editorUi.linkPicker == null)
{
var token = gapi.auth.getToken().access_token;
var view = new google.picker.DocsView(google.picker.ViewId.FOLDERS)
.setParent('root')
.setIncludeFolders(true)
@ -4946,7 +4963,7 @@ var LinkDialog = function(editorUi, initialValue, btnLabel, fn, showPages)
var picker = new google.picker.PickerBuilder()
.setAppId(editorUi.drive.appId)
.setLocale(mxLanguage)
.setOAuthToken(token)
.setOAuthToken(editorUi.drive.token)
.enableFeature(google.picker.Feature.SUPPORT_TEAM_DRIVES)
.addView(view)
.addView(view2)

File diff suppressed because it is too large Load diff

View file

@ -20,12 +20,12 @@ DriveComment.prototype.addReply = function(reply, success, error, doResolve, doR
body.verb = 'reopen';
}
this.file.ui.drive.executeRequest(gapi.client.drive.replies.insert(
this.file.ui.drive.executeRequest(
{
'fileId': this.file.getId(),
'commentId': this.id,
'resource': body
}),
url: '/files/' + this.file.getId() + '/comments/' + this.id + '/replies',
params: body,
method: 'POST'
},
mxUtils.bind(this, function(resp)
{
success(resp.replyId); //pass comment id
@ -39,17 +39,16 @@ DriveComment.prototype.editComment = function(newContent, success, error)
this.file.ui.drive.executeRequest(
this.pCommentId?
gapi.client.drive.replies.patch({
'fileId': this.file.getId(),
'commentId': this.pCommentId,
'replyId': this.id,
'resource': body
}) :
gapi.client.drive.comments.patch({
'fileId': this.file.getId(),
'commentId': this.id,
'resource': body
}),
{
url: '/files/' + this.file.getId() + '/comments/' + this.pCommentId + '/replies/' + this.id,
params: body,
method: 'PATCH'
} :
{
url: '/files/' + this.file.getId() + '/comments/' + this.id,
params: body,
method: 'PATCH'
},
success, error);
};
@ -57,14 +56,13 @@ DriveComment.prototype.deleteComment = function(success, error)
{
this.file.ui.drive.executeRequest(
this.pCommentId?
gapi.client.drive.replies.delete({
'fileId': this.file.getId(),
'commentId': this.pCommentId,
'replyId': this.id
}):
gapi.client.drive.comments.delete({
'fileId': this.file.getId(),
'commentId': this.id
}),
{
url: '/files/' + this.file.getId() + '/comments/' + this.pCommentId + '/replies/' + this.id,
method: 'DELETE'
}:
{
url: '/files/' + this.file.getId() + '/comments/' + this.id,
method: 'DELETE'
},
success, error);
};

View file

@ -71,10 +71,10 @@ DriveFile.prototype.getMode = function()
*/
DriveFile.prototype.getPublicUrl = function(fn)
{
gapi.client.drive.permissions.list(
{
'fileId': this.desc.id
}).execute(mxUtils.bind(this, function(resp)
this.ui.drive.executeRequest({
url: '/files/' + this.desc.id + '/permissions'
},
mxUtils.bind(this, function(resp)
{
if (resp != null && resp.items != null)
{
@ -536,8 +536,11 @@ DriveFile.prototype.isRevisionHistorySupported = function()
*/
DriveFile.prototype.getRevisions = function(success, error)
{
this.ui.drive.executeRequest(gapi.client.drive.revisions.list({'fileId': this.getId()}),
mxUtils.bind(this, function(resp)
this.ui.drive.executeRequest(
{
url: '/files/' + this.getId() + '/revisions'
},
mxUtils.bind(this, function(resp)
{
for (var i = 0; i < resp.items.length; i++)
{
@ -652,9 +655,11 @@ DriveFile.prototype.setDescriptorEtag = function(desc, etag)
*/
DriveFile.prototype.loadPatchDescriptor = function(success, error)
{
this.ui.drive.executeRequest(gapi.client.drive.files.get({'fileId': this.getId(),
'fields': this.ui.drive.catchupFields, 'supportsTeamDrives': true}),
mxUtils.bind(this, function(desc)
this.ui.drive.executeRequest(
{
url: '/files/' + this.getId() + '?supportsTeamDrives=true&fields=' + this.ui.drive.catchupFields
},
mxUtils.bind(this, function(desc)
{
success(desc);
}), error);
@ -712,8 +717,11 @@ DriveFile.prototype.getComments = function(success, error)
return comment;
};
this.ui.drive.executeRequest(gapi.client.drive.comments.list({'fileId': this.getId()}),
mxUtils.bind(this, function(resp)
this.ui.drive.executeRequest(
{
url: '/files/' + this.getId() + '/comments'
},
mxUtils.bind(this, function(resp)
{
var comments = [];
@ -735,8 +743,13 @@ DriveFile.prototype.addComment = function(comment, success, error)
{
var body = {'content': comment.content};
this.ui.drive.executeRequest(gapi.client.drive.comments.insert({'fileId': this.getId(), 'resource': body}),
mxUtils.bind(this, function(resp)
this.ui.drive.executeRequest(
{
url: '/files/' + this.getId() + '/comments',
method: 'POST',
params: body
},
mxUtils.bind(this, function(resp)
{
success(resp.commentId); //pass comment id
}), error);

View file

@ -14,6 +14,17 @@
*/
EditorUi.compactUi = uiTheme != 'atlas';
/**
* Overrides default grid color for dark mode
*/
mxGraphView.prototype.defaultDarkGridColor = '#6e6e6e';
if (uiTheme == 'dark')
{
mxGraphView.prototype.gridColor = mxGraphView.prototype.defaultDarkGridColor;
}
/**
* Switch to disable logging for mode and search terms.
*/
@ -3660,14 +3671,77 @@
this.handleError(resp, title, fn, invokeFnOnClose, notFoundMessage)
}), retry, mxResources.get('changeUser'), mxUtils.bind(this, function()
{
if (this.spinner.spin(document.body, mxResources.get('loading')))
var driveUsers = this.drive.getUsersList();
var div = document.createElement('div');
var title = document.createElement('span');
title.style.marginTop = '6px';
mxUtils.write(title, mxResources.get('changeUser') + ': ');
div.appendChild(title);
var usersSelect = document.createElement('select');
usersSelect.style.width = '200px';
//TODO This code is similar to Dialogs.js change user part in SplashDialog
function fillUsersSelect()
{
this.drive.clearUserId();
gapi.auth.signOut();
usersSelect.innerHTML = '';
// Reload page to reset client auth
window.location.reload();
for (var i = 0; i < driveUsers.length; i++)
{
var option = document.createElement('option');
mxUtils.write(option, driveUsers[i].displayName);
option.value = i;
usersSelect.appendChild(option);
//More info (email) about the user in a disabled option
option = document.createElement('option');
option.innerHTML = '&nbsp;&nbsp;&nbsp;';
mxUtils.write(option, '<' + driveUsers[i].email + '>');
option.setAttribute('disabled', 'disabled');
usersSelect.appendChild(option);
}
//Add account option
var option = document.createElement('option');
mxUtils.write(option, mxResources.get('addAccount'));
option.value = driveUsers.length;
usersSelect.appendChild(option);
}
fillUsersSelect();
mxEvent.addListener(usersSelect, 'change', mxUtils.bind(this, function()
{
var userIndex = usersSelect.value;
var existingAccount = driveUsers.length != userIndex;
if (existingAccount)
{
this.drive.setUser(driveUsers[userIndex]);
}
this.drive.authorize(existingAccount, mxUtils.bind(this, function()
{
if (!existingAccount)
{
driveUsers = this.drive.getUsersList();
fillUsersSelect();
}
}), mxUtils.bind(this, function(resp)
{
this.handleError(resp);
}), true);
}));
div.appendChild(usersSelect);
var dlg = new CustomDialog(this, div, mxUtils.bind(this, function()
{
this.loadFile(window.location.hash.substr(1), true);
}));
this.showDialog(dlg.container, 300, 75, true, true);
}), mxResources.get('cancel'), mxUtils.bind(this, function()
{
window.location.hash = '';
@ -9497,6 +9571,30 @@
// Gets recent colors from settings
ColorDialog.recentColors = mxSettings.getRecentColors();
// Avoids overridden values for changes in
// multiple windows and updates shared values
if (isLocalStorage)
{
try
{
window.addEventListener('storage', mxUtils.bind(this, function(evt)
{
if (evt.key == mxSettings.key)
{
mxSettings.load();
// Updates values
ColorDialog.recentColors = mxSettings.getRecentColors();
this.menus.customFonts = mxSettings.getCustomFonts();
}
}), false);
}
catch (e)
{
// ignore
}
}
// Updates UI to reflect current edge style
this.fireEvent(new mxEventObject('styleChanged', 'keys', [], 'values', [], 'cells', []));
@ -9537,11 +9635,13 @@
/**
* Persists default grid color.
*/
this.editor.graph.view.gridColor = mxSettings.getGridColor();
this.editor.graph.view.gridColor = mxSettings.getGridColor(uiTheme == 'dark');
this.addListener('gridColorChanged', mxUtils.bind(this, function(sender, evt)
{
mxSettings.setGridColor(this.editor.graph.view.gridColor);
console.log('gridColorChanged', this.editor.graph.view.gridColor);
mxSettings.setGridColor(this.editor.graph.view.gridColor, uiTheme == 'dark');
mxSettings.save();
}));

View file

@ -769,7 +769,7 @@ GitHubClient.prototype.showGitHubDialog = function(showFiles, fn)
var content = document.createElement('div');
content.style.whiteSpace = 'nowrap';
content.style.overflow = 'hidden';
content.style.height = '224px';
content.style.height = '304px';
var hd = document.createElement('h3');
mxUtils.write(hd, mxResources.get((showFiles) ? 'selectFile' : 'selectFolder'));
@ -783,7 +783,7 @@ GitHubClient.prototype.showGitHubDialog = function(showFiles, fn)
div.style.padding = '4px';
div.style.overflow = 'auto';
div.style.lineHeight = '1.2em';
div.style.height = '194px';
div.style.height = '274px';
content.appendChild(div);
var listItem = document.createElement('div');
@ -797,7 +797,7 @@ GitHubClient.prototype.showGitHubDialog = function(showFiles, fn)
{
fn(org + '/' + repo + '/' + encodeURIComponent(ref) + '/' + path);
}));
this.ui.showDialog(dlg.container, 340, 270, true, true);
this.ui.showDialog(dlg.container, 420, 360, true, true);
if (showFiles)
{
@ -808,9 +808,10 @@ GitHubClient.prototype.showGitHubDialog = function(showFiles, fn)
{
var link = document.createElement('a');
link.setAttribute('href', 'javascript:void(0);');
link.setAttribute('title', label);
mxUtils.write(link, label);
mxEvent.addListener(link, 'click', fn);
if (padding != null)
{
var temp = listItem.cloneNode();

View file

@ -649,7 +649,7 @@ GitLabClient.prototype.showGitLabDialog = function(showFiles, fn)
var content = document.createElement('div');
content.style.whiteSpace = 'nowrap';
content.style.overflow = 'hidden';
content.style.height = '224px';
content.style.height = '304px';
var hd = document.createElement('h3');
mxUtils.write(hd, mxResources.get((showFiles) ? 'selectFile' : 'selectFolder'));
@ -663,7 +663,7 @@ GitLabClient.prototype.showGitLabDialog = function(showFiles, fn)
div.style.padding = '4px';
div.style.overflow = 'auto';
div.style.lineHeight = '1.2em';
div.style.height = '194px';
div.style.height = '274px';
content.appendChild(div);
var listItem = document.createElement('div');
@ -677,7 +677,7 @@ GitLabClient.prototype.showGitLabDialog = function(showFiles, fn)
{
fn(org + '/' + repo + '/' + encodeURIComponent(ref) + '/' + path);
}));
this.ui.showDialog(dlg.container, 340, 270, true, true);
this.ui.showDialog(dlg.container, 420, 360, true, true);
if (showFiles)
{
@ -688,6 +688,7 @@ GitLabClient.prototype.showGitLabDialog = function(showFiles, fn)
{
var link = document.createElement('a');
link.setAttribute('href', 'javascript:void(0);');
link.setAttribute('title', label);
mxUtils.write(link, label);
mxEvent.addListener(link, 'click', fn);

View file

@ -309,6 +309,7 @@ if (urlParams['offline'] == '1' || urlParams['demo'] == '1' || urlParams['stealt
urlParams['db'] = '0';
urlParams['od'] = '0';
urlParams['gh'] = '0';
urlParams['gl'] = '0';
urlParams['tr'] = '0';
}

View file

@ -1347,8 +1347,7 @@
req.setRequestHeaders = function(request)
{
mxXmlRequest.prototype.setRequestHeaders.apply(this, arguments);
var token = gapi.auth.getToken().access_token;
request.setRequestHeader('authorization', 'Bearer ' + token);
request.setRequestHeader('authorization', 'Bearer ' + editorUi.drive.token);
};
req.send(function(req)

View file

@ -500,7 +500,7 @@ Graph.prototype.createViewState = function(node)
return {
gridEnabled: node.getAttribute('grid') != '0',
//gridColor: node.getAttribute('gridColor') || mxSettings.getGridColor(),
//gridColor: node.getAttribute('gridColor') || mxSettings.getGridColor(uiTheme == 'dark'),
gridSize: parseFloat(node.getAttribute('gridSize')) || mxGraph.prototype.gridSize,
guidesEnabled: node.getAttribute('guides') != '0',
foldingEnabled: node.getAttribute('fold') != '0',

View file

@ -41,13 +41,20 @@ var mxSettings =
{
mxSettings.settings.showStartScreen = showStartScreen;
},
getGridColor: function()
getGridColor: function(darkMode)
{
return mxSettings.settings.gridColor;
return (darkMode) ? mxSettings.settings.darkGridColor : mxSettings.settings.gridColor;
},
setGridColor: function(gridColor)
setGridColor: function(gridColor, darkMode)
{
mxSettings.settings.gridColor = gridColor;
if (darkMode)
{
mxSettings.settings.darkGridColor = gridColor;
}
else
{
mxSettings.settings.gridColor = gridColor;
}
},
getAutosave: function()
{
@ -192,7 +199,8 @@ var mxSettings =
pageFormat: mxGraph.prototype.pageFormat,
search: true,
showStartScreen: true,
gridColor: mxGraphView.prototype.gridColor,
gridColor: mxGraphView.prototype.defaultGridColor,
darkGridColor: mxGraphView.prototype.defaultDarkGridColor,
autosave: true,
resizeImages: null,
openCounter: 0,
@ -308,7 +316,12 @@ var mxSettings =
if (mxSettings.settings.gridColor == null)
{
mxSettings.settings.gridColor = mxGraphView.prototype.gridColor;
mxSettings.settings.gridColor = mxGraphView.prototype.defaultGridColor;
}
if (mxSettings.settings.darkGridColor == null)
{
mxSettings.settings.darkGridColor = mxGraphView.prototype.defaultDarkGridColor;
}
if (mxSettings.settings.autosave == null)

View file

@ -5,6 +5,7 @@ accessDenied=Access Denied
action=Action
actualSize=Actual Size
add=Add
addAccount=Add account
addedFile=Added {1}
addImages=Add Images
addImageUrl=Add Image URL

View file

@ -5,6 +5,7 @@ accessDenied=Access Denied
action=Action
actualSize=Actual Size
add=Add
addAccount=Add account
addedFile=Added {1}
addImages=Add Images
addImageUrl=Add Image URL

View file

@ -5,6 +5,7 @@ accessDenied=Access Denied
action=Action
actualSize=‫الحجم الحقيقي‬
add=‫إضافة‬
addAccount=Add account
addedFile=Added {1}
addImages=‫إضافة صور‬
addImageUrl=‫إضافة رابط صورة‬

View file

@ -5,6 +5,7 @@ accessDenied=Достъпът отказан
action=Action
actualSize=Действителен размер
add=Добавяне
addAccount=Add account
addedFile=Добавен {1}
addImages=Добавяне на изображения
addImageUrl=Добавяне на URL на изображението

View file

@ -5,6 +5,7 @@ accessDenied=Access Denied
action=Action
actualSize=Actual Size
add=Add
addAccount=Add account
addedFile=Added {1}
addImages=Add Images
addImageUrl=Add Image URL

View file

@ -5,6 +5,7 @@ accessDenied=Pristup odbijen
action=Action
actualSize=Prirodna veličina
add=Dodaj
addAccount=Add account
addedFile=Dodato {1}
addImages=Dodaj slike
addImageUrl=Dodaj URL slike

View file

@ -5,6 +5,7 @@ accessDenied=Accés denegat
action=Action
actualSize=Mida real
add=Afegeix
addAccount=Add account
addedFile=Afegit {1}
addImages=Afegeix imatges
addImageUrl=Afegeix l'URL de la imatge

View file

@ -5,6 +5,7 @@ accessDenied=Přístup odepřen
action=Action
actualSize=Skutečná velikost
add=Přidat
addAccount=Add account
addedFile=Přidáno: {1}
addImages=Přidat obrázky
addImageUrl=Přidat URL obrázku

View file

@ -5,6 +5,7 @@ accessDenied=Adgang nægtet
action=Action
actualSize=Faktisk størrelse
add=Tilføj
addAccount=Add account
addedFile=Tilføjet {1}
addImages=Tilføj billeder
addImageUrl=Tilføj billed-URL

View file

@ -5,6 +5,7 @@ accessDenied=Zugriff verweigert
action=Aktion
actualSize=Tatsächliche Größe
add=Einfügen
addAccount=Konto hinzufügen
addedFile={1} eingefügt
addImages=Bilder hinzufügen
addImageUrl=Bild URL hinzufügen

View file

@ -5,6 +5,7 @@ accessDenied=Απαγόρευση εισόδου
action=Action
actualSize=Πραγματικό Μέγεθος
add=Προσθήκη
addAccount=Add account
addedFile=Προστέθηκε {1}
addImages=Προσθήκη εικόνων
addImageUrl=Προσθήκη εικόνας URL

View file

@ -5,6 +5,7 @@ accessDenied=Aliro rifuzita
action=Ago
actualSize=Vera grandeco
add=Aldoni
addAccount=Add account
addedFile={1} aldonita
addImages=Aldoni bildojn
addImageUrl=Add Image URL

View file

@ -5,6 +5,7 @@ accessDenied=Acceso denegado
action=Action
actualSize=Tamaño real
add=Agregar
addAccount=Add account
addedFile=Agregado {1}
addImages=Agregar imágenes
addImageUrl=Añadir URL de la imagen

View file

@ -5,6 +5,7 @@ accessDenied=Ligipääs keelatud
action=Tegevus
actualSize=Tegelik suurus
add=Lisa
addAccount=Add account
addedFile=Lisatud {1}
addImages=Lisa pilte
addImageUrl=Lisa pildi URL

View file

@ -5,6 +5,7 @@ accessDenied=‫دسترسی مجاز نیست.
action=Action
actualSize=‫سایز واقعی‬
add=‫افزودن‬
addAccount=Add account
addedFile=‫اضافه شد {1}
addImages=‫افزودن تصاویر‬
addImageUrl=‫افزودن URL تصویر‬

View file

@ -5,6 +5,7 @@ accessDenied=Pääsy kielletty
action=Action
actualSize=Oikea koko
add=Lisää
addAccount=Add account
addedFile=Lisätty tiedosto
addImages=Lisää kuvia
addImageUrl=Lisää kuvan URL

View file

@ -5,6 +5,7 @@ accessDenied=Tinanggihan ang pagpasok
action=Action
actualSize=Aktwal na sukat
add=Magdagdag
addAccount=Add account
addedFile=Idinagdag
addImages=Magdagdag ng mga imahe
addImageUrl=Magdagdag ng URL ng imahe

View file

@ -5,6 +5,7 @@ accessDenied=Accès refusé
action=Action
actualSize=Taille réelle
add=Ajouter
addAccount=Add account
addedFile=Ajouté(e) {1}
addImages=Ajouter des images
addImageUrl=Ajouter l'URL d'une image

View file

@ -5,6 +5,7 @@ accessDenied=Access Denied
action=Action
actualSize=Actual Size
add=Add
addAccount=Add account
addedFile=Added {1}
addImages=Add Images
addImageUrl=Add Image URL

View file

@ -5,6 +5,7 @@ accessDenied=‫הגישה דחתה‬
action=Action
actualSize=‫גודל אמיתי‬
add=‫הוסף‬
addAccount=Add account
addedFile=‫נוסף {1}
addImages=‫הוסף תמונות‬
addImageUrl=‫הוסף קישור לתמונה‬

View file

@ -5,6 +5,7 @@ accessDenied=Access Denied
action=Action
actualSize=Actual Size
add=Add
addAccount=Add account
addedFile=Added {1}
addImages=Add Images
addImageUrl=Add Image URL

View file

@ -5,6 +5,7 @@ accessDenied=Access Denied
action=Action
actualSize=Actual Size
add=Add
addAccount=Add account
addedFile=Added {1}
addImages=Add Images
addImageUrl=Add Image URL

View file

@ -5,6 +5,7 @@ accessDenied=Hozzáférés elutasítva
action=Action
actualSize=Aktuális méret
add=Hozzáad
addAccount=Add account
addedFile={1} hozzáadva
addImages=Képet hozzáad
addImageUrl=Kép URL hozzáadás

View file

@ -5,6 +5,7 @@ accessDenied=accessDenied
action=action
actualSize=actualSize
add=add
addAccount=addAccount
addedFile=addedFile
addImages=addImages
addImageUrl=addImageUrl

View file

@ -5,6 +5,7 @@ accessDenied=Akses Ditolak
action=Action
actualSize=Ukuran Aktual
add=Tambahkan
addAccount=Add account
addedFile=Ditambahkan
addImages=Tambahkan Gambar
addImageUrl=Tambahkan URL Gambar

View file

@ -5,6 +5,7 @@ accessDenied=Accesso negato
action=Action
actualSize=Dimensioni attuali
add=Aggiungi
addAccount=Add account
addedFile=Aggiunto
addImages=Aggiungi immagini
addImageUrl=Aggiungi immagine da URL

View file

@ -5,6 +5,7 @@ accessDenied=アクセス拒否されました
action=Action
actualSize=実寸
add=追加する
addAccount=Add account
addedFile=追加された {1}
addImages=画像を追加する
addImageUrl=画像のURLを追加する

View file

@ -5,6 +5,7 @@ accessDenied=Access Denied
action=Action
actualSize=Actual Size
add=Add
addAccount=Add account
addedFile=Added {1}
addImages=Add Images
addImageUrl=Add Image URL

View file

@ -5,6 +5,7 @@ accessDenied=접근거부
action=Action
actualSize=실제 크기
add=추가
addAccount=Add account
addedFile=추가된 {1}
addImages=이미지 추가
addImageUrl=이미지 URL 추가

View file

@ -5,6 +5,7 @@ accessDenied=Access Denied
action=Action
actualSize=Actual Size
add=Add
addAccount=Add account
addedFile=Added {1}
addImages=Add Images
addImageUrl=Add Image URL

View file

@ -5,6 +5,7 @@ accessDenied=Access Denied
action=Action
actualSize=Actual Size
add=Add
addAccount=Add account
addedFile=Added {1}
addImages=Add Images
addImageUrl=Add Image URL

View file

@ -5,6 +5,7 @@ accessDenied=Access Denied
action=Action
actualSize=Actual Size
add=Add
addAccount=Add account
addedFile=Added {1}
addImages=Add Images
addImageUrl=Add Image URL

View file

@ -5,6 +5,7 @@ accessDenied=Access Denied
action=Action
actualSize=Actual Size
add=Add
addAccount=Add account
addedFile=Added {1}
addImages=Add Images
addImageUrl=Add Image URL

View file

@ -5,6 +5,7 @@ accessDenied=Akses Ditolak
action=Action
actualSize=Saiz Sebenar
add=Tambah
addAccount=Add account
addedFile=Tambah {1}
addImages=Tambah Imej
addImageUrl=Tambah URL Imej

View file

@ -5,6 +5,7 @@ accessDenied=Toegang geweigerd
action=Actie
actualSize=Ware grootte
add=Toevoegen
addAccount=Add account
addedFile={1} toegevoegd
addImages=Afbeeldingen toevoegen
addImageUrl=Afbeelding-URL toevoegen

View file

@ -5,6 +5,7 @@ accessDenied=Tilgang nektet
action=Action
actualSize=Faktisk størrelse
add=Legg til
addAccount=Add account
addedFile=Fil lagt til
addImages=Legg til bilder
addImageUrl=Legg til bilde-URL

View file

@ -5,6 +5,7 @@ accessDenied=Brak dostępu
action=Action
actualSize=Rozmiar rzeczywisty
add=Dodaj
addAccount=Add account
addedFile=Dodano {1}
addImages=Dodaj obrazy
addImageUrl=Dodaj adres URL obrazu

View file

@ -5,6 +5,7 @@ accessDenied=Acesso Negado
action=Action
actualSize=Tamanho real 
add=Adicionar
addAccount=Add account
addedFile=Adicionado {1}
addImages=Adicionar imagens
addImageUrl=Adicionar URL da imagem

View file

@ -5,6 +5,7 @@ accessDenied=Acesso Negado
action=Action
actualSize=Tamanho real 
add=Adicionar
addAccount=Add account
addedFile=Adicionado {1}
addImages=Adicionar imagens
addImageUrl=Adicionar URL da imagem

View file

@ -5,6 +5,7 @@ accessDenied=Acces interzis
action=Action
actualSize=Dimensiunea reală
add=Adaugă
addAccount=Add account
addedFile=Adăugat {1}
addImages=Adaugă imagini
addImageUrl=Adaugă imagine URL

View file

@ -5,6 +5,7 @@ accessDenied=Доступ запрещён
action=Действие
actualSize=Фактический размер
add=Добавить
addAccount=Add account
addedFile=Добавлено {1}
addImages=Добавить изображения
addImageUrl=Добавить ссылку на изображение

View file

@ -5,6 +5,7 @@ accessDenied=Access Denied
action=Action
actualSize=Actual Size
add=Add
addAccount=Add account
addedFile=Added {1}
addImages=Add Images
addImageUrl=Add Image URL

View file

@ -5,6 +5,7 @@ accessDenied=Access Denied
action=Action
actualSize=Actual Size
add=Add
addAccount=Add account
addedFile=Added {1}
addImages=Add Images
addImageUrl=Add Image URL

View file

@ -5,6 +5,7 @@ accessDenied=Pristup je odbijen
action=Action
actualSize=Prirodna veličina
add=Dodaj
addAccount=Add account
addedFile=Dodat {1}
addImages=Dodaj slike
addImageUrl=Dodaj URL slike

View file

@ -5,6 +5,7 @@ accessDenied=Åtkomst nekad
action=Action
actualSize=Verklig storlek
add=Lägg till
addAccount=Add account
addedFile=Tillagd
addImages=Lägg till bilder
addImageUrl=Lägg till URL för bild

View file

@ -5,6 +5,7 @@ accessDenied=Access Denied
action=Action
actualSize=Actual Size
add=Add
addAccount=Add account
addedFile=Added {1}
addImages=Add Images
addImageUrl=Add Image URL

View file

@ -5,6 +5,7 @@ accessDenied=Access Denied
action=Action
actualSize=Actual Size
add=Add
addAccount=Add account
addedFile=Added {1}
addImages=Add Images
addImageUrl=Add Image URL

View file

@ -5,6 +5,7 @@ accessDenied=Access Denied
action=Action
actualSize=Actual Size
add=Add
addAccount=Add account
addedFile=Added {1}
addImages=Add Images
addImageUrl=Add Image URL

View file

@ -5,6 +5,7 @@ accessDenied=ปฏิเสธการเข้าถึง
action=Action
actualSize=ขนาดปกติ
add=เพิ่ม
addAccount=Add account
addedFile=เพิ่ม {1} แล้ว
addImages=เพิ่มรูปภาพ
addImageUrl=เพิ่ม URL รูปภาพ

View file

@ -5,6 +5,7 @@ accessDenied=erişim engellendi
action=Action
actualSize=Gerçek boyut
add=Ekle
addAccount=Add account
addedFile=Eklendi
addImages=Resim ekle
addImageUrl=Resim URL'si ekle

View file

@ -5,6 +5,7 @@ accessDenied=Доступ заборонено
action=Action
actualSize=Фактичний розмір
add=Додати
addAccount=Add account
addedFile=Додано {1}
addImages=Додати малюнки
addImageUrl=Додати URL-адресу малюнка

View file

@ -5,6 +5,7 @@ accessDenied=Truy cập bị từ chối
action=Action
actualSize=Kích thước thực tế
add=Thêm
addAccount=Add account
addedFile=Đã thêm {1}
addImages=Thêm hình ảnh
addImageUrl=Thêm đường dẫn hình ảnh

View file

@ -5,6 +5,7 @@ accessDenied=拒絕存取
action=Action
actualSize=實際尺寸
add=新增
addAccount=Add account
addedFile=已新增{1}
addImages=新增圖片
addImageUrl=新增圖片URL

View file

@ -5,6 +5,7 @@ accessDenied=访问被拒
action=Action
actualSize=实际尺寸
add=添加
addAccount=Add account
addedFile=已添加 {1}
addImages=添加图片
addImageUrl=添加图片链接