How to stop the user from entering the duplicate record on default save - sugarcrm

I have a custom module where there is an email field. Now i want to stop the user if the email is already in the database.
I want to stop the user on save button and show the error. Like when a required field goes empty.
I tried to get some help but was not able to understand it.

Note: I realized after posting this that you are using suitecrm which this answer will not be applicable toward but I will leave it in case anyone using Sugar has this question.
There are a couple of ways to accomplish this so I'll do my best to walk through them in the order I would recommend. This would apply if you are using a version of Sugar post 7.0.0.
1) The first route is to manually create an email address relationship. This approach would use the out of box features which will ensure your system only keeps track of a single email address. If that would work for your needs, you can review this cookbook article and let me know if you have any questions:
https://support.sugarcrm.com/Documentation/Sugar_Developer/Sugar_Developer_Guide_9.2/Cookbook/Adding_the_Email_Field_to_a_Bean/
2) The second approach, where you are using a custom field, is to use field validation. Documentation on field validation can be found here:
https://support.sugarcrm.com/Documentation/Sugar_Developer/Sugar_Developer_Guide_9.2/Cookbook/Adding_Field_Validation_to_the_Record_View/index.html
The code example I would focus on is:
https://support.sugarcrm.com/Documentation/Sugar_Developer/Sugar_Developer_Guide_9.2/Cookbook/Adding_Field_Validation_to_the_Record_View/#Method_1_Extending_the_RecordView_and_CreateView_Controllers
For your example, I would imagine you would do something like this:
Create a language key for your error message:
./custom/Extension/application/Ext/Language/en_us.error_email_exists_message.php
<?php
$app_strings['ERROR_EMAIL_EXISTS_MESSAGE'] = 'This email already exists.';
Create a custom controller for the record creation (you may also want to do this in your record.js):
./custom/modules//clients/base/views/create/create.js
({
extendsFrom: 'RecordView',
initialize: function (options) {
this._super('initialize', [options]);
//reference your language key here
app.error.errorName2Keys['email_exists'] = 'ERROR_EMAIL_EXISTS_MESSAGE';
//add validation tasks
this.model.addValidationTask('check_email', _.bind(this._doValidateEmail, this));
},
_doValidateEmail: function(fields, errors, callback) {
var emailAddress = this.model.get('your_email_field');
//this may take some time so lets give the user an alert message
app.alert.show('email-check', {
level: 'process',
title: 'Checking for existing email address...'
});
//make an api call to a custom (or stock) endpoint of your choosing to see if the email exists
app.api.call('read', app.api.buildURL("your_custom_endpoint/"+emailAddress), {}, {
success: _.bind(function (response) {
//dismiss the alert
app.alert.dismiss('email-check');
//analyze your response here
if (response == '<email exists>') {
errors['your_email_field'] = errors['your_email_field'] || {};
errors['your_email_field'].email_exists = true;
}
callback(null, fields, errors);
}, this),
error: _.bind(function (response) {
//dismiss the alert
app.alert.dismiss('email-check');
//throw an error alert
app.alert.show('email-check-error', {
level: 'error',
messages: "There was an error!",
autoClose: false
});
callback(null, fields, errors);
})
});
},
})
Obviously, this isn't a fully working example but it should get you most of the way there. Hope this helps!

Related

strapi get related objects of User

I'm using strapi community edition v3.6.8. I have two different models ,User and CarModel. The User Model is strapi's integrated user model. The relation User: CarModel is 1:n
So I've got a profile page in which I want to fetch the User and their related CarModels. I can't get my head around how to achieve this.
I've read several answers that include creating a service which then fetches the related CarModelobjects but I can't figure out what to put into the service.
So the conclusion I've reached so far is that it is probably best if I just create a custom endpoint which fetches the current user and related objects.
How do I go on about this? This is the code I currently have:
axios.get(`http://localhost:1337/users/currentUser`, {
headers: {
Authorization: `Bearer ${token}`
}
})
In extensions/users-permissions/config I've created a routes.json with this content:
"method": "GET",
"path": "/users/currentUser",
"handler": "User.currentUser",
"config": {
"policies": ["policies.isAuthenticated"]
}
}
in config/policies I've created a is-authenticated.js - File with the following content:
module.exports = async (ctx, next) => {
if (ctx.state.user) {
return await next();
}
ctx.unauthorized(`You're not logged in!`);
};
And lastly in extensions/users-permissions/controllers I've created a User.js file with the following content:
const { sanitizeEntity } = require('strapi-utils');
const sanitizeUser = user =>
sanitizeEntity(user, {
model: strapi.query('user', 'users-permissions').model,
});
module.exports = {
currentUser: async (ctx, next) => {
strapi.query('user').find({id: ctx.id}, ['car-model']);
await next();
}
};
So now my questions would be:
1st: Something is wrong because when trying to GET /users/currentUser I get a 403. What exactly am I doing wrong?
2nd: Is this approach even valid in the first place?
And 3rd: What would be the correct approach to solving this problem? Because somewhere else I've read another approach which included writing a custom service which handles resolving the relation, but this looked very complicated imho, considering I'm simply trying to resolve a relation that already exists in the database.
I've also tried manipulating the users/me endpoint which didn't yield any results (and is probably also discouraged).
Interestingly: when the user logs in, I get the user object and all foreign key relations returned. Only when I query /users/me I get only the user data without relations. So I've read that this is a security feature, but what endpoint is used then, when posting to /auth/local and why does this endpoint return the user and related objects?
Could I use this endpoint instead of /users/me?
Any help to this problem would be greatly appreciated, best regards,
deM
So for anyone else looking for a solution, I figured it out. I added a custom route to currentUser as described above then I added a controller for this route in which I put the following code:
currentUser: async (ctx, next) => {
let carModelsOfUser = await (strapi.query('user', 'users-permissions').findOne({id: ctx.state.user.id}, ['carModels', 'carModels.images', 'carModels.ratings.rating']));
return carModelsOfUser;
}
CAUTION!
This also returns the user's hashed password and other potentially sensitive information.
Strapi offers the sanitizeEntity function to remove sensitive information, but as of now I haven't figured out how to use this in that context, as I'm not using the "raw" user here but instead joining some fields.

Safe Methods in Meteor

I'm working on a messaging app using Meteor. I disabled any insert/update/remove called from the client for security reasons. The only way to insert messages now is by using Methods.
Meteor.methods({
sendMessage: function (text) {
Messages.insert({ userId: Meteor.userId(), roomId: Rooms.findOne()._id, name: Meteor.users.find(Meteor.userId()).fetch()[0].profile.name , message: text });
}
});
This approach asks only for the content of the message, so there's no way for a user to call the method using other name or try to send the same message to other Chat Rooms.
I'm a beginner using Meteor so I wonder, wouldn't the real method (not the Stub) which is run on the server get different values from userId and roomId? Rooms.findOne()._id on the server could be any random room document on the db, as well as userId any user.
If this is the case I would have to include extra parameters on the function which would make it much less secure.
I'm probably not understanding about Methods here.
You are on the right track. Using Rooms.findOne() certainly doesn't make sense on the server, and frankly isn't that good on the client either (if you ever publish more that one room this will break). You need to pass both the message and the room id to your method. The method should validate that the insert makes sense. For example, is this user currently in the room. Assuming that's tracked in room.members, sendMessage could be implemented as follows:
Meteor.methods({
sendMessage: function(message, roomId) {
check(message, String);
check(roomId, String);
if (!this.user)
throw new Meteor.Error(401, 'You must be logged in.');
if (_.isEmpty(message))
throw new Meteor.Error(403, 'Message must not be empty.');
var room = Rooms.findOne(roomId);
if (!room)
throw new Meteor.Error(404, 'Room not found.');
if (!_.contains(room.members, this.userId))
throw new Meteor.Error(403, 'You are not in the room.');
var name = Meteor.user().profile.name;
return Messages.insert({
userId: this.userId,
roomId: roomId,
name: name,
message: message
});
}
});
Not all of these checks may be necessary, but this example should give you an idea of the rich set of validations that a method can provide.

Why am I getting this 'undefined' error?

I'm working on a Meteor project, and for some reason this profile template refuses to work.
I'm using the following code, as well as the accounts-password and accounts-entry packages for user management:
this.route('profile', {
path: '/profile/:username',
data: function() {
var userDoc = Meteor.users.findOne({"username": this.params.username});
var bookCursor = Books.find({owner: userDoc._id});
return {
theUser: userDoc,
theBooks: bookCursor
};
}
});
When I try to go to the profile URL for my test accounts ('misutowolf', and 'test2', respectively), I am given the following error in Chrome's dev console: Exception from Deps recompute function: TypeError: Cannot read property '_id' of undefined, pointing to the use of userDoc._id in the call to Books.find().
This makes no sense whatsoever, as I was able to find a user document with the names in question using meteor mongo with both usernames, in the form db.users.find({username: "misutowolf"}) and db.users.find({username: "test2"}).
I am very confused, not sure what is causing this issue at all.
By default Meteor only publish the currently logged in user info via an automatically setup publication.
What you need to do is push to the client the user info (username) you're trying to use, because if you don't do that, the user you're accessing is not published to the client and you get an undefined error when accessing its _id.
First, setup a dedicated publication (on the server) :
Meteor.publish("userByUsername",function(username){
return Meteor.users.find({
username:username
});
});
Then waitOn this publication in your route :
waitOn:function(){
return this.subscribe("userByUsername",this.params.username);
}
Finally, guard against accessing the user document until it is pushed to the client because even if you are waiting on the subscription, the data method might actually get called even if the subscription is not ready yet.
data: function() {
var userDoc = Meteor.users.findOne({"username": this.params.username});
if(!userDoc){
return;
}
// ...
}

Field validations in sugarcrm

I just started using SugarCRM CE for the first time (Version 6.5.15 (Build 1083)). I'm quite impressed with the ease of use when adding new fields or modules, but there's one quite indispensable thing that seems to be missing: Validation of user input.
I would for example like to check a lot of things:
Check if a emailadres has a valid format, using some regular expression
Check if a postalcode exists (maybe do a webswervice call to validate it)
Do a calculation to see if a citizen service number is valid
etc.
The only thing I seem to be able to do in studio is make a field required or not, there doesn't seem to be any standard way to execute a validation on a field.
All I can find when I google on it is lots of ways to hack into the source code, like this one: http://phpbugs.wordpress.com/2010/01/22/sugarcrm-adding-javascript-validation-on-form-submit/ And even then I don't find any examples that actually do a validation.
Am I just missing something? Or is editing source code the only way to add this?
I don't think the "standard" validations are available in the CE edition.
What surprises me is that you can't define a validation somewhere and attach it to a field. I kind of expected this, since the rest of the system is very well structured (modules, packages, etc..)
I now for instance created a 11-check, this is a very specific check for a dutch bank account number. to get this to work, I did the following (based upon examples I found googling around):
I added the bank account to contacts in studio and after that edited \custom\modules\Contacts\metadata\editviewdefs.php
I added the following snippets:
'includes'=> array(
array('file'=>'custom/modules/Contacts/customJavascript.js')),
array (
0 =>
array(
'customCode' =>
'<input title="Save [Alt+S]" accessKey="S" onclick="this.form.action.value=\'Save\'; return check_custom_data();" type="submit" name="button" value="'.$GLOBALS['app_strings']['LBL_SAVE_BUTTON_LABEL']>',
),
1 =>
array(
'customCode' =>
'<input title="Cancel [Alt+X]" accessKey="X" onclick="this.form.action.value=\'index\'; this.form.module.value=\''.$module_name.'\'; this.form.record.value=\'\';" type="submit" name="button" value="'.$GLOBALS['app_strings']['LBL_CANCEL_BUTTON_LABEL'].'">'
)
),
And in customJavascript.js i placed this code:
function check_custom_data()
{
if (!eleven_check(document.getElementById("bankaccount_c").value)){
alert ('Bank account not valid');
return false;
} else {
return check_form('EditView');
}
function eleven_check(bankaccount) {
bankaccount=bankaccount.replace(/\D/, "");
charcount=bankaccount.length;
var som=0;
for (i=1; i<10; i++) {
getal=bankaccount.charAt(i-1);
som+=getal*(10-i);
}
if (som % 11==0 && charcount==9) {
return true
} else {
return false
}
}
}
This check now works the way I want it to work, but I'm wondering if this is the best way to add a validation. this way of adding a validation doesn't however accommodate PHP validations, for instance, if I want to validate against some data in the database for one or another reason, I would have to use ajax calls to get that done.
Email validation is in the pro edition, I had assumed it was in CE as well but I'm not 100% sure.
The other 2 are a lot more specific - postcode validation would depend upon your country so would be difficult to roll out. For these you will need to write your own custom validation.
I know its late, but maybe still someone needs this.
You can just add your custom javascript validation as a callback in your vardefs like this:
'validation' =>
array (
'type' => 'callback',
'callback' => 'function(formname,nameIndex){if($("#" + nameIndex).val()!=999){add_error_style(formname,nameIndex,"Only 999 is allowed!"); return false;}; return true;}',
),
I documented it here as its not well documented elsewhere:
https://gunnicom.wordpress.com/2015/09/21/suitecrm-sugarcrm-6-5-add-custom-javascript-field-validation/
You can add custom validation code to the following file: ./custom/modules/.../clients/base/views/record/record.js
There you can add validation code. In this example, I will validate if the phone_number is not empty when an accounts has a customer-type:
EXAMPLE CODE IN RECORD.JS:
({
extendsFrom: 'RecordView',
initialize: function (options) {
app.view.invokeParent(this, {type: 'view', name: 'record', method: 'initialize', args:[options]});
//add validation
this.model.addValidationTask('check_account_type', _.bind(this._doValidateCheckType, this));
},
_doValidateCheckType: function(fields, errors, callback) {
//validate requirements
if (this.model.get('account_type') == 'Customer' && _.isEmpty(this.model.get('phone_office')))
{
errors['phone_office'] = errors['phone_office'] || {};
errors['phone_office'].required = true;
}
callback(null, fields, errors);
}
})
Don't forget to repair en rebuild!
The full documentation can be found here

Incrementally update Kendo UI autocomplete

I have a Kendo UI autocomplete bound to a remote transport that I need to tweak how it works and am coming up blank.
Currently, I perform a bunch of searches on the server and integrate the results into a JSON response and then return this to the datasource for the autocomplete. The problem is that this can take a long time and our application is time sensitive.
We have identified which searches are most important and found that 1 search accounts for 95% of the chosen results. However, I still need to provide the data from the other searches. I was thinking of kicking off separate requests for data on the server and adding them the autocomplete as they return. Our main search returns extremely fast and would be the first items added to the list. Then as the other searches return, I would like them to add dynamically to the list.
Our application uses knockout.js and I thought about making the datasource part of our view model, but from looking around, Kendo doesn't update based on changes to your observables.
I am currently stumped and any advice would be welcomed.
Edit:
I have been experimenting and have had some success simulating what I want with the following datasource:
var dataSource = new kendo.data.DataSource({
transport: {
read: {
url: window.performLookupUrl,
data: function () {
return {
param1: $("#Input").val()
};
}
},
parameterMap: function (options) {
return {
param1: options.param1
};
}
},
serverFiltering: true,
serverPaging: true,
requestEnd: function (e) {
if (e.type == "read") {
window.setTimeout(function() {
dataSource.add({ Name: "testin1234", Id: "X1234" })
}, 2000);
}
}
});
If the first search returns results, then after 2 seconds, a new item pops into the list. However, if the first search fails, then nothing happens. Is it proper to use (abuse??) the requestEnd like this? My eventual goal is to kick off the rest of the searches from this function.
I contacted Telerik and they gave me the following jsbin that I was able to modify to suit my needs.
http://jsbin.com/ezucuk/5/edit