just two questions :
I have a spreadsheet file editable by multiple user. but I'm afraid it will be annoying:
first question: does an onOpen script run when a user opens a file while another user is editing it? (because my onOpen script resets some cells, so it could be annoying).
So second question: is it possible to simply warn the user, when he tries to open the file, that it is already opened by another user and deny him access?
Thank you for your answers (I already search some answers whithout success)
Loïc
You can also consider using Properties Service which stores simple data in key-value pairs scoped to one script (Script Properties), one user of a script (User Properties), or one document (Document Properties).
I decided to use this Properties Service because your main concern is to prevent the execution of onOpen() which modifies some cells when there is someone already using the file. It is somewhat different with Lock Service, since lock service prevents simultaneous/concurrent execution of a code block.
Sample Implementation:
function onOpen(e){
var ui = SpreadsheetApp.getUi();
// Or DocumentApp or FormApp.
ui.createMenu('LockFile')
.addItem('Lock', 'lock')
.addItem('Unlock', 'unlock')
.addToUi();
//Check if file is locked. if not, execute the procedure
var scriptProperties = PropertiesService.getScriptProperties();
var keys = scriptProperties.getKeys();
if(keys.length==0){
//Lock the file
//lock(); empty Session.getActiveUser().getEmail() received when user opens the spreadsheet
showAlert("Please lock the file first");
SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange('A1').setValue(Session.getActiveUser().getEmail());
SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange('B1').setValue("Test");
}else{
showAlert("File is currenly locked by "+scriptProperties.getProperty('user'));
}
}
function lock(){
var scriptProperties = PropertiesService.getScriptProperties();
var userEmail;
var keys = scriptProperties.getKeys();
Logger.log(keys.length);
if(keys.length==0){
//Create new property with current user's email
userEmail = Session.getActiveUser().getEmail();
Logger.log(userEmail);
scriptProperties.setProperties({user: userEmail});
showAlert("File successfully locked by "+userEmail);
}else{
userEmail = scriptProperties.getProperty('user');
showAlert("File is currenly locked by "+userEmail);
}
}
function unlock(){
var scriptProperties = PropertiesService.getScriptProperties();
var userEmail = Session.getActiveUser().getEmail();
var keys = scriptProperties.getKeys();
Logger.log(keys.length);
Logger.log(scriptProperties.getProperty('user'));
if(keys.length>0){
Logger.log(scriptProperties.getProperty('user'));
if(scriptProperties.getProperty('user') == userEmail){
scriptProperties.deleteAllProperties();
showAlert("File successfully unlocked by "+userEmail);
}else{
showAlert("File is currenly locked by "+scriptProperties.getProperty('user'));
}
}else{
showAlert("File is not locked");
}
}
function reset(){
var scriptProperties = PropertiesService.getScriptProperties();
scriptProperties.deleteAllProperties();
showAlert("Lock has been reset");
}
function showAlert(message) {
var ui = SpreadsheetApp.getUi(); // Same variations.
Logger.log(message);
var result = ui.alert(
'File Lock',
message,
ui.ButtonSet.OK);
}
How it works?
When the file was opened, it will create a custom menu for file lock/unlock. Then check if there is an existing key in the script properties.
If key exist, it will not implement the procedure for setting cell values and will show an alert message. If key doesn't exist, then it will execute the procedure and will ask the current user to lock the file.
I cannot lock the file automatically during onOpen() because during file open, Session.getActiveUser().getEmail() returns an empty string. You can either ask the current user to lock the file using the custom menu or create a prompt dialog and ask for the current user's email as the key value.
You can lock/unlock the file using the custom menu. During file lock, it will check if there is no existing key before locking the file. While during the file unlock, it will check if the current user unlocking the file is the same person who locked the file before unlocking.
You can modify the alert messages based on your preference. This is just a guide on how you could use Properties Service in your goals.
Does an onOpen script run when a user opens a file while another user is editing it?
Yes it will run. According to the official documentation:
The onOpen(e) trigger runs automatically when a user opens a
spreadsheet, document, presentation, or form that they have permission
to edit.
Is it possible to simply warn the user, when he tries to open the file, that it is already opened by another user and deny him access?
No, but what you can do is to use LockService which:
Prevents concurrent access to sections of code. This can be useful
when you have multiple users or processes modifying a shared resource
and want to prevent collisions.
There are many resources on how to use LockService and the solutions depends on your specific use case, so I would advice you to look for these resources, do some research and then ask a specific question regarding that if you are not able to implement it yourself.
Resources:
How to understand LockService and implement it correctly?
Related
I'm having a problem with the following thing.
In Dynamics CRM I have a Custom Button that created a new Order, the button works great, however, I wanted to make it work as the OOB one and prefilled some of the Account information such as Account Name and Price List (form example).
example
In Ribbon Workbench I've added the following parameter for my button
ribbon settins
The next thing that I've done was to create a new Jscript web resource and add the following code.
references: https://learn.microsoft.com/en-us/dynamics365/customerengagement/on-premises/developer/customize-dev/pass-dynamics-365-data-page-parameter-ribbon-actions
Web resource name: new_getorder
Code: function XrmCore.Commands.Open.openNewRecord(primaryControl) {
var formContext = primaryControl;
}
I've then added the web resource on the Order form where is supposed to trigger when clicking on the custom button from the account form, but I'm getting a script error (which is not a surprise for me)
form properies
I've also enabled Pass execution context as first parameter
Web resource method does not exist: XrmCore.Commands.Open.openNewRecord
new order button from account ribbon
Error when loading the Order form: Web resource method does not exist: XrmCore.Commands.Open.openNewRecord
Instead of custom code, why don't you try Map Entity Field feature?
You can map attributes between entities that have an entity
relationship. This lets you set default values for a record that is
created in the context of another record.
In your scenario, after mapping Account and Price list fields between Account and Order tables, when you will go to the Related > Orders section and try to create an Order from there, those two fields should be pre-filled on the new Order form.
And I think this feature would work also when you have a Orders sub grid on account form and try to create Order from new button on the sub grid.
You may go through the documentation to understand this concept and to check steps to create mapping.
Anyway, I've managed to find my solution by using this script in a custom web resource
enter code here
var entityFormOptions = {};
entityFormOptions["entityName"] = "salesorder";
entityFormOptions["createFromEntity"] = currentRecordRef;;
// Open the form.
Xrm.Navigation.openForm(entityFormOptions).then(
function (success) {
console.log(success);
},
function (error) {
console.log(error);
});
I'm having an issue when running a function in Cloud Code. It is supposed to check the existence of an object and, if it does exist, create a new user:
Parse.Cloud.define("createUser", function(request, response) {
// Query the existing company by id
var query = new Parse.Query(Parse.Object.extend("Company"));
query.equalTo("objectId", request.params.company.existing.id);
query.find().then(function(result){
var user = new Parse.User();
user.set("username", request.params.username);
user.set("password", request.params.username);
user.set("email", request.params.email);
user.set("permissions", ["User"]);
var company = result[0];
user.signUp(null, {
success: function(user){
// Asign company ACL for User write permission
var cACL = company.getACL();
cACL.setWriteAccess(user.id, true);
company.setACL(cACL);
// Save company
company.save();
console.log(company);
// Establish user-company relationship
var cRelation = user.relation("associated");
cRelation.add(company);
// Save user
user.save();
console.log(user);
// Finish
response.success(user);
},
error: function(user, error){
response.error(JSON.stringify({code: -8000, message: "User creation failed"}));
}
});
}, function(error){
response.error(JSON.stringify({code: -8001, message: "Invalid company"}));
});
});
I first query Parse for the existence of said object. If it does exist I create a new user with the parameters received. In the completion block of the user creation I assign the proper ACLs (to the company object) and later on save them. That's when I encounter the first issue: the ACLs are not saved (checked in the dashboard). I console.log the company for debugging purposes and it shows the ACLs are correctly set. So I assume it must be a saving problem.
NOTE: The user is created, but whatever I try to do later doesn't work.
Later on I add this object to a relationship previously defined in the dashboard, but I have the same problem with that: the relationship does not come up in the dashboard, even though when I console.log the object it shows that the relationship was properly set.
I'm lost here. I don't understand why this isn't working and I've read tons of online documentation and still can't find the answer.
Okay, after a day of work I finally found out my problem. I had ACLs set everywhere and I had no privilege for saving the objects I was trying to save. So saving was indeed failing.
I should note that if you are having the same problem I did, you can easily solve it using the Master Key. To do so, you need to call Parse.Cloud.useMasterKey() before executing any requests that must be authenticated.
This only works in Cloud Code, and you should definitely know what you are doing when you use the Master Key because it basically gives read and write privileges to anyone, everywhere, for everything. So make sure your logic is flawless because you might get big security problems if it's not used wisely. As Uncle Ben said: With great power comes great responsibility.
Hope this helps someone.
I want to restrict users from creating account such that they cannot create accounts starting with some character. For example if I say R, then user should not be able to create accounts like Rtest1 or Rrest123, but can create accounts like testR1. Where can I apply this check? I checked the invalid character constraints in design forms for account form, but that does not allow me to specify that character anywhere in the username for example if I give R then it won't allow testR1 too. I need something like String.StartsWith() in ITIM using some policy or custom javascript. Note:- I cannot use workflows for this.
So I found a solution for this using provisioning policy, I am posting the solution as a reference for others. Create a entitlement in PP for the service/s you want to apply this check on. Once done select that entitlement and click parameters to create a parameter enforcement for this entitlement. Select the attribute you want to enforce this check on (for me it was eruid) select enforcement type as mandatory and select javascript option. Enter the following script:-
var accountId = parameters.eruid[0]; //gets the eruid
//check if account start with Q
if (accountId != null && accountId.length > 0 && (accountId.toLowerCase().substr(0, 1)=='q')) {
return accountId.substr(1,accountId.length-1) //remove the q from beginning and return the new id as a suggestion to user
}
return accountId; //or else return the same id
This blocks user requests starting with q and does not allow you to submit add account requests. See the caption below:-
I need to create a User-Specific content page, I have registered users and I need to create a module/page/menu/feed or whatever that displays articles that are for only one or some users.
For example, in my site, I show to public some projects (articles redacted as some architecturing project) for example: news, finished projects, etc, some people register and can access to other content that is intended for registered users, for example: new projects that only registered clients would be interested, so far, I managed to make this work, but now, I have work that is for a specific user and he/she only can see it in his/her feed, for example: status of their project made by us, some galleries with photos of the work in progress, etc.
So, This feed must be a module/page/menu that shows articles with a specific category and for registered users. But I want to be able to post an article that is only intended for a specific user or a set of users and shows in this feed (module) or at least a whole new module that shows user-specific content without having to create a category and an access level for each user.
Is there an extension or plug-in that helps me do this?
you need to make do two things:
Make a new user-group (Users->User group) as a sub-group of
registered. Add the users that need the special permission to this
group.
Make a new access-level (Users->access-levels) and add you
new user-group to this access level
Set the access-level on the article(s) that you want to restrict access on.
The module should check the access-level before it displays it to the users, and only display the restricted articles to those that have the correct privileges.
The system don't really support what you ask for, but you could use the following setup:
Make a content-plugin (http://docs.joomla.org/J2.5:Creating_a_content_plugin). In the event onContentPrepareForm, you modify the created_by_alias-field to use it for your special user allowed to view this content article.
function onContentPrepareForm($form, $data){
// first check that context is right
// Then change the type of the field. This should allow to select
// the user when you create the article.
$form->getField('created_by_alias')->__set('type', 'user');
}
In the event onContentPrepareData, check if the data in the created_by_alias references a valid user in the user group your users shoud be in
public function onContentPrepare($context, $article, $params, $page){
// first check that context is right
//Then fetch the user data:
if(is_int($article->created_by_alias)){ // ( Optionally check if the article is in a specific category )
$db=JFactory::getDbo();
$currentuser=JFactory::getUser();
$allowedgroup=2; // the registered users group
$sql="select u.id from #__users inner join #__user_usergroup_map ug (u.id=ug.user_id) where ug.group_id={$allowedgroup}";
$db->setQuery($sql);
$user=$db->loadObject();
if($user->id==$currentuser){
$data->user=$user;
}
else{
//Unset the article content to prevent unothorized users from seeing the content.
unset($article);
}
}
Finally, create a module (http://docs.joomla.org/Creating_a_simple_module) that feeds articles for a particular user, containing at least:
$list=array();
$user=Jfactory::getUser();
if($user->id>0){
$sql="select * from #__content where created_by_alias={$user->id}";
// ( also add sql to check that the user has access,
// the article is published, the correct category etc)
$db=Jfactory::getDbo();
$db->setQuery($sql);
$list=$db->loadObjectList();
if(!count($list)) return;
}
else return;
print_r($list); // Prints the content articles
This scheme should protect unauthorized users from viewing the content of the articles ment for a specific user. The module should (after nicely outputting what you want to display) display a list of articles for the logged inn user. The module can link to the article in the normal way, and the authorized user will have access. So it should be "safe enough", and provide the functionality you need without too much hassle.
I'll try anotherone here:
You could just install a messageing system on your site. UddeIM is one such system. This will allow you to make specific content for your users. UddeIM can be configured so only administrators can send messages. There is also some modules for this component to show latest messages etc.
Hye all.
I have a gui program. My question is how can i make my program started with user insert his name first. For example: when the user double click on my program, then there will be a popup message ask the user his name. After the user insert his name, then only the program will run. Can anyone help me on this?
I think what you are looking for is the inputdlg function. It pops up a dialog box that asks for input from the user. And you can set the WindowStyle as a "modal" box, so that nothing else can be done until it is satisfied.
You can use the following code to do what you want, just put it at the beginning of the file. This will ask for a name as woodchips suggests, when it is empty or cancel is clicked, the dialog will reappear until a non-empty name is given.
%% user authentication
user = '';
while isempty(user)
user = inputdlg('Please enter your user name:',...
'User name',1,{''});
if isempty(user)
user = '';
else
user = deblank(user{1});
end;
end
%% real program code below
However, if all you need is the user name (i.e. account name) of your user, you can do this automatically without any user intervention:
if ispc
user = getenv('UserName');
else
user = getenv('USER');
end;
In Unix/Linux and probably Mac you should be able to probe this a little more by using the finger program (when present on your system). Take a look at the manpage of finger and the matlab command system for more information.