Docuemt postopen event not operating on profile document - xpages

I need to save serial number of the document in a profile document and here is a code of action Execute Script:
if (document1.isNewNote()){
var pdoc:NotesDocument=database.getProfileDocument("LastNumber","")
var lnm=pdoc.getItemValue("lastNumber")[0];
var inputText6:com.ibm.xsp.component.xp.XspInputText = getComponent("inputText6");
inputText6.setValue(lnm);
pdoc.replaceItemValue("lastNumber",lnm);
pdoc.save();
}
This code is not opening profile document at all. Any thing wrong in the code?

"LastNumber" is the name of the form used to create Profile Document ?
this profile document already exist ?
there are no reader fields in this profile document ?
you have an error on this line : var pdoc:NotesDocument=database.getProfileDocument("LastNumber","") ?
or you have debug it and see that pdoc is null ?
instead of pdoc.getItemValue("lastNumber")[0] you can use pdoc.getItemValueInteger("lastNumber") to get a typed result
I supposed that this field contains a number and you want to increment it
instead of using inputText field you can set value directly with document1.setValue("NumberField", lnm);

I second the caution Per is suggesting. Profile documents can be a beast. You should abstract access to the "next number" into a SSJS function call. Btw. in your code snippet you don't actually increment the last number. Also: if your input text control is bound, go after the data source, not the UI.
A crude way (I would use a managed application bean for better isolation) for a better function could be this:
if(document1.isNewNote() {
document1.setValue("DocumentNumber",applicationTools.getNextNumber());
}
Then in a SSJS library you would have:
var applicationTools = {
"getNextNumber" : function() {
synchronized(applicationScope){
var pdoc:NotesDocument=database.getProfileDocument("LastNumber","");
if (!applicationScope.lastNumber) {
applicationScope.lastNumber = pdoc.getItemValueInteger("lastNumber");
}
applicationScope.lastNumber++;
pdoc.replaceItemValue("lastNumber",applicationScope.lastNumber);
pdoc.save(); //Make sure pdoc is writeable by ALL!!!!
pdoc.recycle();
return applicationScope.lastNumber;
}
},
"someOtherUtility" : function(nameToLookup, departments) {
// more stuff here
}
}
Which, in some way has been asked before, but not for a profile field. Someone still could simply go after the applicationScope.lastNumber variable, which is one of the reasons why I rather use a bean. The other: you could do the saving asynchronously, so it would be faster.
Note: in any case the number generation only works when you have a non-replicating database. But abstracting the function opens the possibility to replace fetching the number from the profile with a call to a central number generator ... or any other mechanism ... without changing your form again.

Related

How to override template file item-list.html.twig for field_slider_images in Drupal 8?

I want to override the item listing template file core/themes/classy/templates/dataset/item-list.html.twig for listing the fields field_slider_images as well as field_blog_tags respectively of their's multiple values of the field.
I have selected "Unordered List" in the view.
Please do check the attached image.
I have created following files :
item-list--field-blog-tags.html.twig
item-list--field-slider-images.html.twig
But, this is not rendered for the listing of the fields.
When I have created item-list.html.twig then only it will access.
However, both fields have different data to style and I am not able to get the current field name which is loading it's data in item-list.html.twig.
Had a brief look at this and it doesn't seem that 'item-list' to have suggestions, which is quite unfortunate.
In this situation there are two options:
Create your own suggestion which would accomplish exactly what you need.
You'll have to do something like this:
/
/*add new variable to theme with suggestion name*/
function hook_theme_registry_alter(&$theme_registry) {
$theme_registry['item_list']['variables']['suggestion'] = '';
}
//send a value to newly added variable to use it build the suggestion
function hook_ENTITY_TYPE_view(array &$build, $entity, $display, $view_mode) {
//add condition here if field exists or whatever, do the same for other field
$build['field_slider_images']['#suggestion'] = 'field_slider_images';
}
//use newly added variable to build suggestion
function hook_theme_suggestions_THEME_HOOK(array $variables) {//THEME_HOOK=item_list
$suggestions = array();
if(isset($variables['suggestion'])){
$suggestions[] = 'item_list__' . $variables['suggestion'];
}
return $suggestions;
}
Now you should be able to use item-list--field-slider-images.html.twig
Second option is to do what others in core did: use a new theme
function hook_ENTITY_TYPE_view(array &$build, $entity, $display, $view_mode) {
//add condition here if field exists or whatever, do the same for other field
$build['field_slider_images']['#theme'] = array(
'item_list',
'item_list__field_slider_images',
);
}

JSON-RPC and handle to current document

Good day folks!
I have a call (CSJS) to a JSON-RPC service in my XPage to get the value of a viewScope variable. I use this call to cycle through my field names (dynamically) and validate them.
I'm having trouble figuring out how to get a handle to the datasource (document1 in my case) to which these fields are bound, from the RPC service.
Here's the call:
// Use the JSON-RPC Service to get the number of asset item rows
// from the viewScope variable
var deferred = myRPCService.getScopeVar();
deferred.addCallback(function(result){
alert(result); // <-- viewScope variable value
// get the dynamic field names for the asset items based on row #
var itemname = '';
for (var i = 1; i < result; i++) {
var itemname = 'replace'+(i < 10? '0':'')+ i
if (document1.getItemValueString(itemname) == ""){
// do this
} else{
// do that
}
}
});
I do get back the value of the viewScope variable from the RPC call but I can't get beyond that. Any pointers/examples would be appreciated.
Thanks,
Dan
PS: See my comment below ...
You don't say what calls the JSON-RPC event, so I will guess and say it is a button. You will have to have an additional serverside event set the viewScope variable to the value of document1.getItemValueString(itemname) which you can then retrieve in the RPC.
Your SSJS event code will look something like:
viewScope.result = document1.getItemValueString(itemname);
The additional event can be onmouseover (not recommended) or onkeypress. I assume you are using onclick clientside for this code, you can't use the onclick serverside because this code would need to run prior to your clientside. You will want the viewScope to contain fresh data, hence this suggestion. If the data doesn't need to be fresh, then set it on page load.
Another idea would be to call two RPC's. The first one can set the viewScope variable and the second can use it. The first one won't need a callback, and just calls a java or SSJS function that sets the viewScope. Personally I like this better than my first suggestion.

Write the plugin when Selecting the lookup flied and according to filed selection Show/Hide Address filed in form.....?

We Have Contact Entities in contact Entitie one lookup filed company Name in that lookup having two values 1.Account and 2.Contact . When we are selecting contact show the address filed when we select account hide the address filed we needs to write the plugin to Execute that works. Kindly any one help me on the same.
Thanks!!
Rajesh Singh
First, if you need to make change on a form, you can't use plug-ins. Plug-ins are made for bussinees logics, like Update another record when the first one is created, make complex logic, etc...
What you need it is a javascript that executes on the OnLoad of the form and OnChange event in that OptionSet.
The lines you are looking for are:
function ShowField()
{
// The field is present on the form so we can access to its methods
var optionSet = Xrm.Page.getAttribute("custom_attribute");
if (optionSet == undefined)
{
return;
}
// The control is present on the form so we can access to its methods
var controlAddress = Xrm.Page.getControl("custom_address");
if (controlAddress == undefined)
{
return;
}
var valueOptionSet = optionSet.getValue(); // This is the value you set creating the OptionSet
switch (valueOptionSet)
{
case 0: // Your account value, show
controlAddress.setVisible(true);
case 1: // Your contact value, hide
controlAddress.setVisible(false);
default:
return;
}
}
You need to register a web resource and register the event, if you need more information about the code or why this stuff is here and why this not just tell me.

MongoDB Database Semaphores and Node.js Process.NextTick()

This may be a vary bad idea, or a possible solution that we have to a database concurrency problem.
We have a method that is called to do an update of a mongo record. We are seeing some concurrency problems - process A reads the record, process B reads the record, process A makes mods and saves the record, process makes B mods and saves the record. Because B reads after A, before A writes, it doesn't know about the changes A made, and we lose the data from A.
I'm wondering if we could not use a database semaphore, basically a field on the collection, that is a boolean. If we read the record at the start of the method, and the field is true, it's being edited. At that point, re-call the method using process.nexttick(), with the same data. Otherwise, set the semaphore, and carry on.
There would still be a bit of time between the read and the save, but it should be/could be faster than what we are doing now.
Be something like this. Any thoughts, anyone done anything like this? Will it even work?
function remove_source(service_id,session, next)
{
var User = Mongoose.model("User");
/* get the user, based on the session user id */
User.findById(session.me,function(err,user_info)
{
if (user_info.semaphore === true)
{
process.nextTick(remove_source(service_id,session,next));
}
else
{
user_info.semaphore = true;
user_info.save(function(err,user_new)
{
if (err) next(err,user_new);
else continue_on(null,user_new);
});
}
function continue_on(user_new)
{
etc.......
}
Edit: New Code:
The function now looks as follows. I'm doing individual updates to the arrays. This of course means that I now have the possibility, if the transaction fails between the first and second transactions, of having data out of sync. I'm thinking that I could simply resave the user object that I retrieved on entry into the function, overwriting my changes. I don't know if Mongoose/Mongo will not do the save if I have not changed that object, will have to try and see. Any more thoughts?
var User = Mongoose.model("User");
/* get the user, based on the session user id */
User.findById(session.me,function(err,user_info)
{
if (err)
{
next(err,user_info,null);
return;
}
if (!user_info || user_info.length === 0)
{
next(_e("ACCOUNT_NOT_FOUND"),"user_id: " + session.me);
return;
}
var source_service_info = _.where(user_info.credentials, {"source_service_id": service_id});
var source_service = source_service_info.source_service;
User.findByIdAndUpdate(session.me,{$pull: {"credentials": {"source_service_id": service_id}}},{},function(err,user_credential_removed)
{
if (err)
{
next(err,user_info,null);
return;
}
User.findByIdAndUpdate(session.me,{$pull: {"criteria": {"source_service": source_service}}},{},function(err,user_criteria_removed)
{
if (err)
{
next(err,user_info,null);
return;
}
else
{
next(null,user_criteria_removed);
}
});
});
});
};
The problem with your approach is that it just shortens the time during which the data could be read by a second process, it doesn't eliminate the problem.
The solution to this would be to set your semaphore in the same action as the read. I haven't used Mongoose, but in MongoDB you can use findAndModify to only return a User record if the semaphore is false, and if it is false, in one atomic operation, set the semaphore to true.
If you don't want to use findAndModify, you could first do an update that sets the semaphore true (or to some specific ID value so you know that it is YOUR semaphore) only if the semaphore is not set. Then, if that process succeeds, you could do the find (perhaps passing your semaphore ID as a criterion in the find). However, findAndModify, if it is available in Mongoose, would do that in one step.
A variation of that is described here: http://docs.mongodb.org/manual/tutorial/isolate-sequence-of-operations/ where you do a form of optimistic locking that checks that the old values are unchanged before changing them to the new values.
There is a variation on this that uses a separate table to simulate a two-phase commit: http://docs.mongodb.org/manual/tutorial/perform-two-phase-commits/
Edited: Upon interchange below, this seems to be a schema and updating issue. Question may become something like: I have some entries in an array, and the ordinal index to those entries relates to some other arrays as well. How do I perform deletes without having mismatches?
Three off the top possibilities occur, depending on frequency in the real world vs QA test scenarios.
Consider adding a deleted flag but keeping the records in the same order. If someone toggles, reuse the same record, but fix however you want.
Use an associative array (JS object) for each element (not a feature from relational world.) If you need an order, add an array that lists the keys in order. Both have syntax to update without touching anything other that what has changed, and will not overwrite changes to different fields.
Use an associative array where the keys are numbers. Actual deletion won't hurt retrieval.
stuff = {}
stuff[1] = {some:'details'}
stuff[2] = {some:'details2'}
Was
1) Are you making changes to the same field? Make that into an array, and push changes, and pop the latest to read the current value.
2) Are you changing different fields, but data is getting trounced? Then there is better syntax to use for the updating. you can update field by field.
$set: { 'fielda': 'valuea' }
won't lose edits on previous fields
3) change your schema
4) change the timing on the processes so they don't overlap. Or so they do so in smaller subsets, that you can manage to prevent from overlapping.
I'd like to know, just out of interest, what multiple processes are needed to make updates on the same record? I don't work with anything that looks like that.

MODX - Making multiple snippet calls on a page returning differeny output

I've created a snippet that pulls data from a databse table and displays it in tabular format. The snippet takes an id as parameter, and this is added to the sql query.
My problem is that if I've got more than 1 snippet call (sometimes need the tabular data for different id's displayed on a page) on the same page, all table data is the same as the last database call that's been made by the last snippet.
What do I need to do to kinda not cache the snippet database calls and have them all display their own content?
I've tried setting the page to no cache-able. Also used the [! !] brackets for the snippet calls, and even used the function_exists() method, but none of them helped.
Please can someone help me?
thanks
Try this at the end of the snippet:
mysql_connect('host', 'user', 'pass');
mysql_select_db('db_name');
You need to specify the connection parameters ofcourse.
It would help to answer if you can post your snippet. I do this with multiple calls on the page without issue, so there is either something wrong inside the snippet, or you need to output to unique placeholder names.
You have encountered a glitch of ModX, and it took me a long time to solve. ModX does a lot of caching by using hashing and apparently, when multiple connections are made from within one page divided over multiple snippets, this erratic behaviour can be seen. This is most likely very unwanted behaviour, it can be solved easily but gives you terrible headache otherways.
One sympton is that $modx->getObject($classname, $id)returns null (often).
The solution is very simple:
either use a static class with a single db instance, or
use $modx->setPlaceholder($instance, $tag);, or a combination.
My solution has been:
class dt__xpdo {
private function __construct() {}
public function __destruct() {
$this->close();
}
static public function db($modx = null) {
if ($modx->getPlaceholder('dt_xpdo') == '') {
$dt_user = 'xxxxxxxxx';
$dt_pw = 'xxxxxxxxx';
$dt_host = 'localhost';
$dt_dbname = 'xxxxxxxxx';
$dt_port = '3306';
$dt_dsn = "mysql:host=$dt_host;dbname=$dt_dbname;port=$dt_port;charset=utf8";
$dt_xpdo = new xPDO($dt_dsn, $dt_user, $dt_pw);
$dt_xpdo->setPackage('mymodel', MODX_CORE_PATH.'components/mymodel/'.'model/', '');
//$modx->log(modX::LOG_LEVEL_DEBUG, 'mymodel.config.php');
//$modx->log(modX::LOG_LEVEL_DEBUG, 'Could not addPackage for mymodel!');
$modx->setPlaceholder('dt_xpdo', $dt_xpdo);
}
return $modx->getPlaceholder('dt_xpdo');
}
}
Now you can use in your code:
require_once 'above.php';
and use something like
$xpdo = dt__xpdo::db($modx);
and continue flawlessly!

Resources