Suitescript 2.0 Adding 3rd party libraries - suitescript2.0

I have been watching the instructional videos on Youtube from Stoic Software and have tried uploading the following to my Netsuite account to test the creation of third party libraries:
/**
* Prompts the user if the current project has not been re-baselined in some time
*
* #copyright 2020 Stoic Software, LLC
* #author Eric T Grubaugh <eric#stoic.software>
*
* #NApiVersion 2.x
* #NScriptType ClientScript
* #NModuleScope Public
* #NAmdConfig ./amdconfig.json
* #appliedtorecord job
*/
define(["moment"], (moment) => {
const message = "Project has not been re-baselined in over two months.";
function pageInit(context) {
let lastBaseline = moment(
context.currentRecord.getValue({ fieldId: "lastbaselinedate" })
);
if (lastBaseline.isValid() && moment().diff(lastBaseline, "months") >= 2) {
alert(message);
}
}
return { pageInit };
});
This is the amdconfig.json file that sits in the same location as the script:
{
"paths": {
"moment": "./SuiteScripts/sdf_ignore/moment-with-locales.js"
}
}
When I try to create the script record, I get the following error:
Row 14 is the following: define(["moment"], (moment) => {
Can anyone see what the issue is?
Edit: thanks to #fullstack.studio I was able to upload the script.
I am getting the following error message though where it is not recognising the function:
the third party library I am trying to use is the one found under:
https://momentjs.com/

I use path without '.' => /SuiteScripts/.... The '.' is a ref to the current folder. And you may remove the .js ext.
"paths": {
"helper": "/SuiteScripts/My_Helper"
}
In the meta try to use 2.1 instead 2.x
/**
....
* #NApiVersion 2.1
....
*/

Related

Drupal 8: When I update the node field to a specific value, how to call my module (managefinishdate.module) to update another field?

I am having a node type with machine name to_do_item, and I want to create a module called managefinishdate to update the node: when a user edit the node's field (field_status) to "completed" and click "save", then the module will auto update the field_date_finished to current date.
I have tried to create the module and already success to install in "Extend":
function managefinishdate_todolist_update(\Drupal\Core\Entity\EntityInterface $entity) {
...
}
however, I am not sure is this module being called, because whatever I echo inside, seems nothing appeared.
<?php
use Drupal\Core\Entity\EntityInterface;
use Drupal\node\Entity\Node;
/** * Implements hook_ENTITY_TYPE_update().
* If a user update status to Completed,
* update the finished date as save date
*/
function managefinishdate_todolist_update(\Drupal\Core\Entity\EntityInterface $entity) {
$node = \Drupal::routeMatch()->getParameter('node');
print_r($node);
//$entity_type = 'node';
//$bundles = ['to_do_item'];
//$fld_finishdate = 'field_date_finished';
//if ($entity->getEntityTypeId() != $entity_type || !in_array($entity->bundle(), $bundles)) {
//return;
//}
//$current_date=date("Y-m-d");
//$entity->{$fld_finishdate}->setValue($current_date);
}
Following Drupal convention, your module named 'manage_finish_date' should contain a 'manage_finish_date.module file that sits in the root directory that should look like this:
<?php
use Drupal\node\Entity\Node;
/**
* Implements hook_ENTITY_TYPE_insert().
*/
function manage_finish_date_node_insert(Node $node) {
ManageFinishDate::update_time();
}
/**
* Implements hook_ENTITY_TYPE_update().
*/
function manage_finish_date_node_update(Node $node) {
ManageFinishDate::update_time();
}
You should also have another file called 'src/ManageFinishDate.php' that should look like this:
<?php
namespace Drupal\manage_finish_date;
use Drupal;
use Drupal\node\Entity\Node;
class ManageFinishDate {
public static function update_time($node, $action = 'create') {
// Entity bundles to act on
$bundles = array('to_do_item');
if (in_array($node->bundle(), $bundles)) {
// Load the node and update
$status = $node->field_status->value;
$node_to_update = Node::load($node);
if ($status == 'completed') {
$request_time = Drupal::time();
$node_to_update->set('field_date_finished', $request_time);
$node_to_update->save();
}
}
}
}
The code is untested, but should work. Make sure that the module name, and namespace match as well as the class filename and class name match for it to work. Also, clear you cache once uploaded.
This will handle newly created and updated nodes alike.
Please look after this sample code which may help you:
function YOUR_MODULE_entity_presave(Drupal\Core\Entity\EntityInterface $entity) {
switch ($entity->bundle()) {
//Replace CONTENT_TYPE with your actual content type
case 'CONTENT_TYPE':
$node = \Drupal::routeMatch()->getParameter('node');
if ($node instanceof \Drupal\node\NodeInterface) {
// Set the current date
}
break;
}
}

API Platform and custom POST operation with custom body

I hope I'm right to ask this. I've looked at (almost) all similar concern but I ain't satisfied yet.
I'm working on a User entity and for days (weeks actually) now i'm trying to POST a user with a custom body. Here's some part of my entity User :
/**
* #ApiResource(
* normalizationContext={"groups"={"read"}},
* denormalizationContext={"groups"={"write"}},
* itemOperations={
* "get",
* "put",
* "delete",
* "get_active_user"={
* "method"="GET",
* "path"="/users/active/me",
* "controller"=UserReadAction::class,
* "defaults"={"_api_receive"=false},
* "swagger_context"={
* "parameters"={
*
* }
* }
* },
* },
* collectionOperations={
* "change_password"={
* "method"="POST",
* "path"="/users/active/changepassword",
* "controller"=UserChangePasswordAction::class,
* "normalization_context"={"groups"={"afup"}},
* "defaults"={"_api_receive"=false},
* "swagger_context"={
* "summary" = "Change user password",
* "parameters"={
* {
* "name" = "User",
* "in" = "body",
* "schema" = {
* "type" = "object",
* "properties" = {
* "password" = {"type"="string"},
* "nom" = {"type"="string"},
* }
* },
* "required" = "true",
* }
* },
* }
* }
* }
* )
* #ORM\Entity(repositoryClass="App\Repository\UserRepository")
* #ORM\Table(name="users")
*/
class User implements UserInterface
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
* #Groups({"read", "write", "afup"})
*/
private $id;
Here is the controller:
namespace App\Controller\SDK;
use App\Entity\User;
use App\Service\SDK\UserService;
use Symfony\Component\Security\Core\Security;
class UserChangePasswordAction
{
public function __invoke(User $data)
{
var_dump($data);die;
}
}
And the services.yaml (some part) file
services:
# default configuration for services in *this* file
_defaults:
autowire: true # Automatically injects dependencies in your services.
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.
public: false # Allows optimizing the container by removing unused services; this also means
# fetching services directly from the container via $container->get() won't work.
# The best practice is to be explicit about your dependencies anyway.
# makes classes in src/ available to be used as services
# this creates a service per class whose id is the fully-qualified class name
App\:
resource: '../src/*'
exclude: '../src/{DependencyInjection,Entity,Migrations,Tests,Kernel.php}'
# controllers are imported separately to make sure services can be injected
# as action arguments even if you don't extend any base controller class
App\Controller\:
resource: '../src/Controller/*'
tags: ['controller.service_arguments']
When I try this (see var_dump in controller), i get an error saying:
Cannot autowire argument $data of "App\Controller\SDK\UserChangePasswordAction()": it references class "App\Entity\User" no such service exists
I read the official doc and it seems that the _invoke method should automatically retrieve the entity. But it does not work for me.
Notice: I also defined a custom item operation "get_active_user" and it works fine.
Please I would like to understand :
what I did wrong,
how it actually works,
Thank you.
EDIT:
In the collectionOperation definition, i removed the following setting which means that we manually want to handle data (User) retrieval :
"defaults"={"_api_receive"=false},
Now, the controller returns an empty User entity, not an error. I still can't get the submitted data.
The edit of my question fix the concern. Actually, I just needed to remove this annotation from the POST opration definition :')
"defaults"={"_api_receive"=false},
Now, when I submit the data, I get them as on the following image :
This annotation is important when you write custom GET operation.
It is not working because that is a CollectionOperation. In this case, you can get the user through TokenStorageInterface
namespace App\Controller\SDK;
use App\Entity\User;
use App\Service\SDK\UserService;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
class UserChangePasswordAction
{
private $tokenStorage;
public function __construct(TokenStorageInterface $tokenStorage)
{
$this->tokenStorage = $tokenStorage;
}
public function __invoke(Request $request) //Get request if you want o keep args empty
{
var_dump($this->tokenStorage->getToken()->getUser());die;
}
}

react-native .toLocaleString() not working on android

Updated 2022: With hermes enabled you should be good now.
I'm using .toLocaleString() on react-native for my number output. All work on IOS but seems not working on Android. This is normal or? Do I need to use a function for the decimal?
rather than using a polyfill or an external dependency, change the JSC your android app builds with. For the newer versions of react-native add or override the following line in app/build.gradle
def jscFlavor = 'org.webkit:android-jsc-intl:+'
On newer versions of RN >0.62 you can change the JSC (JavaScriptCore) build variant to support/include ICU i18n library and necessary data allowing to use e.g. Date.toLocaleString and String.localeCompare
Replace this line in your android/app/build.gradle file
def jscFlavor = 'org.webkit:android-jsc:+'
with this line
def jscFlavor = 'org.webkit:android-jsc-intl:+'
Clean build and react-native run android
Note
This variant is about 6MiB larger per architecture than default.
So, expect your APK size to increase by about 4MB for each APK architecture build if using def enableSeparateBuildPerCPUArchitecture = true and a more bigger APK if separate build per architecture is disabled
You can use
number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")
This is an issue with Javascript core used to run react native in Android and not with react native itself. To overcome this, you'll have to integrate latest javascript core into your android build or upgrade react native to 0.59.
The details are documented in JSC Android Buildscripts repo.
Now for people who would like to do the locale string formatting without needing to integrate the entire javascript core, Javascript has Internationalization API which lets you format numbers to language sensitive format. Documentation available at MDN
This API is not available in android and needs to be polyfilled using Intl
In your project root, install the Intl library
yarn add intl
And then in your project's index file (index.js) add the following code at the top of the file:
if(Platform.OS === 'android') { // only android needs polyfill
require('intl'); // import intl object
require('intl/locale-data/jsonp/en-IN'); // load the required locale details
}
After doing the above two steps, you can now get locale string anywhere in your project using
new Intl.NumberFormat('en-IN', { style: 'currency', currency: 'INR' }).format(10000000);
In case you need to format number for another locale code, all the locale code details are available under the intl/locale-data/jsonp/ directory. Simply require the ones you need in your index.js file.
The reason for this is very old version of JavaScriptCore used by react-native. iOS embeds own version which is why it is working fine there.
Issue still exists (some reading about where it's heading https://github.com/facebook/react-native/issues/19737)
And more info about this from Airbnb devs
https://medium.com/airbnb-engineering/react-native-at-airbnb-the-technology-dafd0b43838 (search for "JavaScriptCore inconsistencies")
(value) => {
if (typeof value === 'number') {
const [currency, cents] = (value / 100).toFixed(2).toString().split('.');
return `${currency.replace(/\B(?=(\d{3})+(?!\d))/g, '.')},${cents}`;
}
return '0,00';
}
it's more recent and lightweight, please check
First install:
yarn add #formatjs/intl-getcanonicallocales #formatjs/intl-locale #formatjs/intl-pluralrules #formatjs/intl-numberformat
Check if need polyfill
import {shouldPolyfill} from '#formatjs/intl-numberformat/should-polyfill'
if (shouldPolyfill()) {
require('#formatjs/intl-getcanonicallocales/polyfill');
require('#formatjs/intl-locale/polyfill');
require('#formatjs/intl-pluralrules/polyfill');
require('#formatjs/intl-numberformat/polyfill');
require('#formatjs/intl-numberformat/locale-data/en-US');
}
see source: https://formatjs.io/docs/polyfills/intl-numberformat/
A very easy and straight forward way is to use a polyfill:
First it needs to be installed:
npm i number-to-locale-string-polyfill
This has to be added in your code, best just outside the class/function where you want to use .toLocaleString().
require('number-to-locale-string-polyfill');
I solved this using a custom function
function numberToMoney(amount, simbol = '$', decimalCount = 2, decimal
= ".", thousands = ",") {
decimalCount = Math.abs(decimalCount)
decimalCount = isNaN(decimalCount) ? 2 : decimalCount
const negativeSign = amount < 0 ? "-" : ""
const i = parseInt(amount = Math.abs(Number(amount) ||
0).toFixed(decimalCount)).toString()
const j = (i.length > 3) ? i.length % 3 : 0
return simbol + negativeSign + (j ? i.substr(0, j) + thousands : '') +
i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + thousands) + (decimalCount ?
decimal + Math.abs(amount - i).toFixed(decimalCount).slice(2) : "")
};
No need to install extra packages
Displaying currency values in React Native A zero dependencies solution:
const parseCurr = (value) =>
Platform.OS === 'android'
? '$' + price.toFixed(2)
: price.toLocaleString('en-US', { style: 'currency', currency:'USD' });
parseCurr(25.75) // => $25.75
A real life example (money values are multiplied by 100 for better cents precision) and converting the value to Brazilian Reais (R$)
export const getBRPrice = (price: number) => {
const parsedPrice =
( price / 100 ).toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' });
return Platform.OS === 'android'
? `R$${ ( price / 100 ).toFixed(2) }`
: parsedPrice;
};
// getBRPrice(450) => R$4,50
Solution: 1
Go to your android/app/build.gradle
Replace this line def jscFlavor = 'org.webkit:android-jsc:+'
with this
def jscFlavor = 'org.webkit:android-jsc-intl:+'
Stop the metro and rebuild your app.
Solution: 2
Otherwise, you can use this package https://www.npmjs.com/package/luxon
import import {DateTime} from 'luxon';
const date = DateTime.fromISO(new Date().toISOString());
const formatted = date.toLocaleString(DateTime.DATETIME_MED);
console.log(formatted);
Merging some responses from this thread, you can use this code where it is possible to customize the formatted response
const defaultOptions = {
significantDigits: 2,
thousandsSeparator: ',',
decimalSeparator: '.',
symbol: '$'
}
const currencyFormatter = (value, options) => {
if (typeof value !== 'number') value = 0.0
options = { ...defaultOptions, ...options }
value = value.toFixed(options.significantDigits)
const [currency, decimal] = value.split('.')
return `${options.symbol} ${currency.replace(
/\B(?=(\d{3})+(?!\d))/g,
options.thousandsSeparator
)}${options.decimalSeparator}${decimal}`
}
function numberWithCommas(x) {
return x.toString().replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ",");
}
This will remove commas after decimal point
If you need two digits after the decimal and always want to round down
you can use below code.
Math.floor(1233.31231231 * 100) / 100).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",")
To round differently check out this resource
If these solutions don't work for you... In my case, I was using React Native with the expo web simulator and wanted to format minutes with 2 characters ie. 00, 01, ... 10, 11, etc. My solution was to check if minutes contained one character, if so, prepend a "0".
... + (date.getMinutes().toString().length == 1 ? "0" : "") + date.getMinutes().toString()

Rdflib.js, how to serialize the data into turtle (.ttl) format?

How can I serialize RDF in turtle using rdflib.js? There's not much documentation. I can use:
Serializer.statementsToN3(destination);
to serialize into the N3 format, but not much besides that. I've tried altering the aforementioned command to stuff like statementsToTtl/Turtle/TURTLE/TTL, but nothing seems to work.
Figured it out. Courtesy of this (secret) Github gist.
$rdf.serialize(undefined, source, undefined,` 'text/turtle', function(err, str){
// do whatever you want, the data is in the str variable.
})
This is the code from the aforementioned Github gist.
/**
* rdflib.js with node.js -- basic RDF API example.
* #author ckristo
*/
var fs = require('fs');
var $rdf = require('rdflib');
FOAF = $rdf.Namespace('http://xmlns.com/foaf/0.1/');
XSD = $rdf.Namespace('http://www.w3.org/2001/XMLSchema#');
// - create an empty store
var kb = new $rdf.IndexedFormula();
// - load RDF file
fs.readFile('foaf.rdf', function (err, data) {
if (err) { /* error handling */ }
// NOTE: to get rdflib.js' RDF/XML parser to work with node.js,
// see https://github.com/linkeddata/rdflib.js/issues/47
// - parse RDF/XML file
$rdf.parse(data.toString(), kb, 'foaf.rdf', 'application/rdf+xml', function(err, kb) {
if (err) { /* error handling */ }
var me = kb.sym('http://kindl.io/christoph/foaf.rdf#me');
// - add new properties
kb.add(me, FOAF('mbox'), kb.sym('mailto:e0828633#student.tuwien.ac.at'));
kb.add(me, FOAF('nick'), 'ckristo');
// - alter existing statement
kb.removeMany(me, FOAF('age'));
kb.add(me, FOAF('age'), kb.literal(25, null, XSD('integer')));
// - find some existing statements and iterate over them
var statements = kb.statementsMatching(me, FOAF('mbox'));
statements.forEach(function(statement) {
console.log(statement.object.uri);
});
// - delete some statements
kb.removeMany(me, FOAF('mbox'));
// - print modified RDF document
$rdf.serialize(undefined, kb, undefined, 'application/rdf+xml', function(err, str) {
console.log(str);
});
});
});

Trying to get the Grails ldap-0.8.2 plugin to work for non-authentication searching of AD

I've been trying to get the ldap-0.8.2 or gldapo plugin to work with Grails 2.3.5 to perform a simple person search in AD. I'm not looking for authentication, just to build a person directory search form. I have close to a week now looking at old references to problems implementing this plugin and just can not seem to figure out what the right combination of fiery hoops to jump through are.
In BuildConfig.groovy I have:
compile ":ldap:0.8.2"
In Config.groovy I have:
import edu.fgcu.gtd.GldapoUser
ldap {
directories {
directory1 {
defaultDirectory = true
url = "ldap://FGCU-AMBERJACK.primary.ad.fgcu.edu"
userDn = "CN=******,OU=******,OU=******,OU=******,DC=**,DC=**,DC=***,DC=***"
password = "********"
searchControls {
countLimit = 40
timeLimit = 600
searchScope = "subtree"
}
}
}
schemas: [edu.fgcu.gtd.GldapoUser]
}
I have the following groovy file at path "Ldap/edu/fgcu/gtd/GldapoUser.groovy"
package edu.fgcu.gtd
import gldapo.schema.annotation.GldapoNamingAttribute
import gldapo.schema.annotation.GldapoSynonymFor
import gldapo.schema.annotation.GldapoSchemaFilter
/**
*
* #author pallen
*/
#GldapoSchemaFilter("(objectclass=person)")
class GldapoUser {
#GldapoSynonymFor("uid")
String username
#GldapoSynonymFor("cn")
String name
#GldapoSynonymFor("title")
String title
#GldapoSynonymFor("physicalDeliveryOfficeName")
String office
#GldapoSynonymFor("telephoneNumber")
String phone
#GldapoSynonymFor("mail")
String email
#GldapoSynonymFor("department")
String department
}
And then I have the following controller
package edu.fgcu.gtd
import edu.fgcu.gtd.GldapoUser
class PersonSearchController {
def index() {
render(view: "search")
}
def search() {
String searchString = params?.lastName + "*"
if (params.firstName){
searchString += "," + params.firstName + "*"
}
def List personSearchList = GldapoUser.findAll(
base: "OU=Florida Gulf Coast University,DC=primary,DC=ad,DC=fgcu,DC=edu") {
like "cn", searchString
}
respond personSearchList, model:[personSearchCount: personSearchList.count()]
}
}
When I run the application I receive the following error, which I have seen others reference, but none of the suggestions that I have found so far have helped me resolve this.
URI: /GroovyGTD/personSearch/search
Class: groovy.lang.MissingMethodException
Message: No signature of method: static edu.fgcu.gtd.GldapoUser.findAll() is applicable for argument types: (java.util.LinkedHashMap, edu.fgcu.gtd.PersonSearchController$_search_closure1) values: [[base:OU=Florida Gulf Coast University,DC=primary,DC=ad,DC=fgcu,DC=edu], ...] Possible solutions: findAll(), findAll(groovy.lang.Closure), find(), find(groovy.lang.Closure)
I'm relatively new to Grails, but am fairly adept with Java, and have worked through some difficult configurations for external libraries, but this plugin has me stumped.
Thanks in advance,
Paul
I was able to get it all to work.
First issue was the schemas comment. I had to put schemas = [ edu.fgcu.gtd.GldapoUser] in config.groovy.
Next I had to add a #GldapoNamingAttribute to my GldapoUser object for the "cn" attribute, and "uid" is not in my AD person entry so I got rid of it and used the "sAMAccountName" for username.
It is all working well after those few changes.