Using Persistent Flash Message Library for ColdFusion - coldfusion

I am trying to use a library for showing Flash Messages https://github.com/elpete/flashmessage But I am having trouble getting it working correctly. The documentation isn't that great and I am new to ColdFusion. I want to have the ability to have persistent error messages across pages. Specifically during checkout so when the user needs to go back or a validation error occurs the message will appear. According to the documentation:
The FlashMessage.cfc needs three parameters to work:
A reference to your flash storage object. This object will need
get(key) and put(key, value) methods. A config object with the
following properties: A unique flashKey name to avoid naming
conflicts. A reference to your containerTemplatePath. This is the view
that surrounds each of the individual messages. It will have
references to a flashMessages array and your messageTemplatePath. A
reference to your messageTemplatePath. This is the view that
represents a single message in FlashMessage. It will have a reference
to a single flash message. The name is chosen by you in your container
template. Create your object with your two parameters and then use it
as normal.
I am getting the error
the function getMessages has an invalid return value , can't cast null value to value of type [array]
I had this script somewhat working at one point but it seems very finicky. I believe it is my implementation of it. I am hoping someone here can help me figure out where I went wrong. Or give me some pointers because I am not sure I am even implementing it correctly.
This is What I have in my testing script:
<cfscript>
alertStorage = createObject("component", 'alert');
config = {
flashKey = "myCustomFlashKey",
containerTemplatePath = "/flashmessage/views/_templates/FlashMessageContainer.cfm",
messageTemplatePath = "/flashmessage/views/_templates/FlashMessage.cfm"
};
flash = new flashmessage.models.FlashMessage(alertStorage, config);
flash.message('blah');
flash.danger('boom');
</cfscript>
And inside of alert.cfc I have:
component {
public any function get(key) {
for(var i = 1; i < ArrayLen(session[key]); i++) {
return session[key][i];
}
}
public any function put(key, value) {
ArrayAppend(session.myCustomFlashKey, value);
return true;
}
public any function exists() {
if(structKeyExists(session,"myCustomFlashKey")) {
return true;
} else {
session.myCustomFlashKey = ArrayNew();
return false;
}
}
}
The Flash Message Component looks like this:
component name="FlashMessage" singleton {
/**
* #flashStorage.inject coldbox:flash
* #config.inject coldbox:setting:flashmessage
*/
public FlashMessage function init(any flashStorage, any config) {
instance.flashKey = arguments.config.flashKey;
singleton.flashStorage = arguments.flashStorage;
instance.containerTemplatePath = arguments.config.containerTemplatePath;
instance.messageTemplatePath = arguments.config.messageTemplatePath;
// Initialize our flash messages to an empty array if it hasn't ever been created
if (! singleton.flashStorage.exists(instance.flashKey)) {
setMessages([]);
}
return this;
}
public void function message(required string text, string type = "default") {
appendMessage({ message: arguments.text, type = arguments.type });
}
public any function onMissingMethod(required string methodName, required struct methodArgs) {
message(methodArgs[1], methodName);
}
public any function render() {
var flashMessages = getMessages();
var flashMessageTemplatePath = instance.messageTemplatePath;
savecontent variable="messagesHTML" {
include "#instance.containerTemplatePath#";
}
setMessages([]);
return messagesHTML;
}
public array function getMessages() {
return singleton.flashStorage.get(instance.flashKey, []);
}
private void function setMessages(required array messages) {
singleton.flashStorage.put(
name = instance.flashKey,
value = arguments.messages
);
}
private void function appendMessage(required struct message) {
var currentMessages = getMessages();
ArrayAppend(currentMessages, arguments.message);
setMessages(currentMessages);
}
}

Related

React Native JSI: How to expose a native object to javascript code

I use RN 0.66.3 and want to make direct sync calls from javascript to native code in my iOS React Native project to share data without using the React Native Bridge for performance purposes so that I need to have a shared global object and access to its properties and methods from javascript.
I know that is possible with JSI (JavaScript Interface) but there are no docs and few tutorials about so what the simple steps or sample code to implement this?
To expose your object to javascript over React Native JSI you should make next steps:
Make your c++ class inherited from HostObject
Override get and set methods to implement access to its properties and methods.
Install your object globally on React Native runtime.
Look at NativeStorage sample that can store key/value pairs persistently to NSUserDefaults across launches of your app:
NativeStorage class
#include <jsi/jsi.h>
#import <React/RCTBridge+Private.h>
using namespace facebook::jsi;
using namespace std;
// Store key-value pairs persistently across launches of your app.
class NativeStorage : public HostObject {
public:
/// Stored property
int expirationTime = 60 * 60 * 24; // 1 day
// Helper function
static NSString* stringValue(Runtime &runtime, const Value &value) {
return value.isString()
? [NSString stringWithUTF8String:value.getString(runtime).utf8(runtime).c_str()]
: nil;
}
Value get(Runtime &runtime, const PropNameID &name) override {
auto methodName = name.utf8(runtime);
// `expirationTime` property getter
if (methodName == "expirationTime") {
return this->expirationTime;
}
// `setObject` method
else if (methodName == "setObject") {
return Function::createFromHostFunction(runtime, PropNameID::forAscii(runtime, "setObject"), 2,
[](Runtime &runtime, const Value &thisValue,const Value *arguments, size_t count) -> Value {
NSString* key = stringValue(runtime, arguments[0]);
NSString* value = stringValue(runtime, arguments[1]);
if (key.length && value.length) {
[NSUserDefaults.standardUserDefaults setObject:value forKey:key];
return true;
}
return false;
});
}
// `object` method
else if (methodName == "object") {
return Function::createFromHostFunction(runtime, PropNameID::forAscii(runtime, "object"), 1,
[](Runtime &runtime, const Value &thisValue,const Value *arguments, size_t count) -> Value {
NSString* key = stringValue(runtime, arguments[0]);
NSString* value = [NSUserDefaults.standardUserDefaults stringForKey:key];
return value.length
? Value(runtime, String::createFromUtf8(runtime, value.UTF8String))
: Value::undefined();
});
}
return Value::undefined();
}
void set(Runtime& runtime, const PropNameID& name, const Value& value) override {
auto methodName = name.utf8(runtime);
// ExpirationTime property setter
if (methodName == "expirationTime") {
if (value.isNumber()) {
this->expirationTime = value.asNumber();
}
}
}
// Install `nativeStorage` globally to the runtime
static void install(Runtime& runtime) {
NativeStorage nativeStorage;
shared_ptr<NativeStorage> binding = make_shared<NativeStorage>(move(nativeStorage));
auto object = Object::createFromHostObject(runtime, binding);
runtime.global().setProperty(runtime, "nativeStorage", object);
}
};
AppDelegate.mm
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
...
// Runtime notification
[NSNotificationCenter.defaultCenter addObserverForName:RCTJavaScriptDidLoadNotification object:nil queue:nil
usingBlock:^(NSNotification* notification) {
RCTCxxBridge* cxxbridge = (RCTCxxBridge*)notification.userInfo[#"bridge"];
if (cxxbridge.runtime) {
NativeStorage::install(*(Runtime*)cxxbridge.runtime);
}
}];
return YES;
}
App.js
nativeStorage.expirationTime = 1000;
console.log(nativeStorage.expirationTime);
const key = "greeting";
nativeStorage.setObject(key, "Hello JSI!");
const text = nativeStorage.object(key);
console.log(text);
Outputs:
1000
Hello JSI!
Future React Native's TurboModules & CodeGen makes all of this cleaner & easier but it's the low level JSI implementation of the native module that can be called directly from JavaScript without going through the React Native Bridge.
Note: Since the sample uses JSI for synchronous native methods access, remote debugging (e.g. with Chrome) is no longer possible. Instead, you should use Flipper for debugging your JS code.

How to fill state/region/subregion selections when page is opened in edit mode?

In Laravel 8/livewire 2/aplinejs I need to fill state/region/subregion, which are tables in db
and I select subregion from priorly selected regions and select regions from priorly selected, like
public function updatedSelectedState($state_id)
{
$this->regionsSelectionArray = Region::getRegionsSelectionArray($state_id, 'A');
$this->detailsForm['state_id'] = $state_id;
}
public function updatedSelectedRegion($region_id)
{
$this->subregionsSelectionArray = Subregion::getSubregionsSelectionByRegionIdArray($region_id);
$this->selectedSubregion = null;
}
it works ok for data inserting , but when I open editor in “edit” mode and I neeed to fill initvalue I
failed how do it. I remember when I made similar tasks with jquery I have common bool var which I set to true
when jquery was inited. And in onChange event I checked this var .
But how can I do it in livewire ?
Thanks in advance!
Call the below functions in your edit method
Example:
public function updatedSelectedState($state_id)
{
$this->changeSelectedState($state_id);
}
public function updatedSelectedRegion($region_id)
{
$this->changeSelectedRegion($region_id);
}
public function changeSelectedState($state_id){
$this->regionsSelectionArray = Region::getRegionsSelectionArray($state_id, 'A');
$this->detailsForm['state_id'] = $state_id;
}
public function changeSelectedRegion($region_id)
{
$this->subregionsSelectionArray = Subregion::getSubregionsSelectionByRegionIdArray($region_id);
$this->selectedSubregion = null;
}
public function edit(){
// $state_id = provide your state id here
// $region_id = provide your region id here
$this->changeSelectedState($state_id);
$this->changeSelectedRegion($region_id);
}

Rename feature for EMF Resources

I'm working with a project, where I have EMF model 'A' which is referenced in many other models 'B','C'... etc. What I want is I want to give a rename feature for these resources. So when user renames 'A', its references have to be updated.
Please provide some idea on it, if there is any frame work for this or I have to get all the references and then programmatically iterate and update the references.
I solved the same problem in another way.
The fundamental problem is that a referenced resource file might be renamed, and this breaks the references.
Instead of a refactoring that automatically updates all references I created a Repair File References command, which the user can invoke on an edited model.
The command performs these steps:
Prompts the user to select a missing resource to repair
Prompts the user to select a replacement file
Updates all objects in the model that has a proxy URI that matches the missing resource. Replaces proxies with resolved objects in the new resource.
If you still want to make a refactoring instead, I think you anyway can use my code as a starting point.
/**
* Locates and fixes unresolved references in a model.
*/
public class ReferenceRepairer {
public static final String COMMAND_ID = Activator.PLUGIN_ID + ".commands.repairReferences";
/**
* 1) Prompts the user to select a missing resource to repair
* 2) Prompts the user to select a replacement file
* 3) Updates all objects in the model with a proxy URI that matches the missing resource. Replaces proxies
* with resolved objects in the new resource.
*/
public static void repairResourceReference(Shell shell, EditingDomain editingDomain) {
Resource res = promptMissingResource(shell, editingDomain);
if (res == null) return;
IFile newFile = promptReplacementFile(shell);
if (newFile == null) return;
repairReferences(editingDomain, res, URI.createPlatformResourceURI(newFile.getFullPath().toString(), true));
}
private static void repairReferences(final EditingDomain editingDomain, Resource missingRes, final URI newUri) {
URI missingUri = missingRes.getURI();
// Create new resource for the replacement file
Resource newRes = editingDomain.getResourceSet().getResource(newUri, true);
Map<EObject, Collection<Setting>> proxies = UnresolvedProxyCrossReferencer.find(editingDomain.getResourceSet());
CompoundCommand repairRefsCommand = new CompoundCommand("Repair references") {
/**
* Disallow undo. The model changes could be undone, but it seems impossible to
* recreate a non-existent resource in the resource set.
*/
#Override
public boolean canUndo() {
return false;
}
};
// Resolve all proxies from this resource and repair reference to those objects
for (Entry<EObject, Collection<Setting>> entry : proxies.entrySet()) {
EObject proxy = entry.getKey();
URI proxyUri = EcoreUtil.getURI(proxy);
if (!proxyUri.trimFragment().equals(missingUri)) continue;
EObject resolved = newRes.getEObject(proxyUri.fragment());
if (resolved.eIsProxy()) continue;
// Update all objects that have references to the resolved proxy
for (Setting sett : entry.getValue()) {
if (sett.getEStructuralFeature().isMany()) {
#SuppressWarnings("unchecked")
EList<Object> valueList = (EList<Object>) sett.get(true);
int proxyIx = valueList.indexOf(proxy);
repairRefsCommand.append(SetCommand.create(editingDomain,
sett.getEObject(), sett.getEStructuralFeature(), resolved, proxyIx));
} else {
repairRefsCommand.append(SetCommand.create(editingDomain,
sett.getEObject(), sett.getEStructuralFeature(), resolved));
}
}
}
if (!repairRefsCommand.isEmpty()) {
editingDomain.getCommandStack().execute(repairRefsCommand);
}
// Remove the
editingDomain.getResourceSet().getResources().remove(missingRes);
}
private static IFile promptReplacementFile(Shell shell) {
ElementTreeSelectionDialog dialog = new ElementTreeSelectionDialog(shell,
new WorkbenchLabelProvider(), new WorkbenchContentProvider());
dialog.setInput(ResourcesPlugin.getWorkspace().getRoot());
dialog.setTitle("Select Replacement Resource");
dialog.setMessage("Select a file which will replace the missing file.");
dialog.setValidator(new ISelectionStatusValidator() {
#Override
public IStatus validate(Object[] selection) {
if (selection.length == 0 || !(selection[0] instanceof IFile)) {
return ValidationStatus.error("The selected object is not a file.");
}
return new Status(IStatus.OK, Activator.PLUGIN_ID, "");
}
});
if (dialog.open() != Window.OK) return null;
return (IFile) dialog.getFirstResult();
}
private static Resource promptMissingResource(Shell shell, EditingDomain editingDomain) {
ElementListSelectionDialog dialog = new ElementListSelectionDialog(shell,
new LabelProvider() {
#Override
public String getText(Object elem) {
return ((Resource) elem).getURI().toString();
}
})
{
/** Make dialog OK button enabled when there are errors, instead of vise-versa. */
#Override
protected void updateButtonsEnableState(IStatus status) {
Button okButton = getOkButton();
if (okButton != null && !okButton.isDisposed()) {
okButton.setEnabled(!status.isOK());
}
}
/** Disable filter text field */
#Override
protected Text createFilterText(Composite parent) {
Text text = super.createFilterText(parent);
text.setSize(0, 0);
text.setLayoutData(GridDataFactory.swtDefaults().exclude(true).create());
text.setVisible(false);
return text;
}
};
dialog.setTitle("Select Missing Resource");
dialog.setMessage(
"Select a URI of a missing resource file that should be replaced by an URI to an existing file.");
dialog.setElements(getMissingResources(editingDomain.getResourceSet().getResources()).toArray());
if (dialog.open() != Window.OK) return null;
return (Resource) dialog.getFirstResult();
}
private static List<Resource> getMissingResources(List<Resource> resources) {
List<Resource> missingResources = new ArrayList<>();
for (Resource res : resources) {
try {
if (res.getURI().isPlatformPlugin()) continue;
URL url = FileLocator.toFileURL(new URL(res.getURI().toString()));
java.net.URI uri = new java.net.URI(url.getProtocol(), "", "/" + url.getPath(), null);
if (!Files.exists(Paths.get(uri))) {
missingResources.add(res);
}
} catch (InvalidPathException | IOException | URISyntaxException exc) {
// Ignore. There mighe be weird Sirius resource in the resources set which we can't recognice
}
}
return missingResources;
}
}

Naming Blob Dynamically for WebJob on a Schedule

I have a web job which is creating a blob based on the return value of a WebClient call. This is working fine. But as you can see from the Blob attribute (see code below), the name of the file is static. So, it is getting overwritten every time in blob storage.
Function class:
public class Functions
{
private static int _retryCount;
private static readonly int _retryLimit = int.Parse(ConfigurationManager.AppSettings["retryLimit"]);
private static readonly string _ghostRestfullUri = ConfigurationManager.AppSettings["ghostRestfullUri"];
[NoAutomaticTrigger]
public static void LightUpSite([Blob("ghost/response.json")] out string output, TextWriter logger)
{
_retryCount = 0;
output = string.Empty;
do
{
try
{
using (var request = new WebClient())
{
var response = request.DownloadString(_ghostRestfullUri);
_retryCount++;
output = response;
break;
}
}
catch(Exception exception)
{
logger.WriteLine("Job failed. Retry number:{0}", _retryCount);
}
} while (_retryCount < _retryLimit);
}
}
Main menu:
public class Program
{
static void Main()
{
var host = new JobHost();
host.Call(typeof(Functions).GetMethod("LightUpSite"));
}
}
How can I use placeholders to dynamically name the incoming file?
I have already tried the following:
ghost/{name}
ghost/{BlobName}
Other things to note:
This job is run on a schedule, so the host does not run and block
This job does not get invoked by a trigger, it just wakes up and runs;
Because the source is not coming from a message queue object or a uploaded file, I can’t figure out how I am supposed to name this blob.
Perhaps somehow using the blob storage API directly?
To name an output blob dynamically use IBinder as shown in this sample
To name an input blob dynamically as in a call from from Host.Call just pass the name of blob as argument:
static void Main()
{
var host = new JobHost();
host.Call(typeof(Functions).GetMethod("LightUpSite"), new {blobArgumentName= "container/blob"});
}

How do I provide ObjectContent that is a string

I'm writing a unit test which tests the scenario where a body is sent in the request which is a plain string, i.e. not parseable as JSON.
In this test, I'm setting the HttpRequestMessage something like this:
var ojectContent = new ObjectContent(typeof(string)
, "aaaaa"
, new JsonMediaTypeFormatter());
httpRequestMessage.Content = objectContent;
The problem is, when I debug the code, the request body has been set to "aaaaa" (note the additional quotes) which is enough to cause the deserialisation code to treat the request body differently, meaning I can't test what I mean to test. I need the request body to be aaaaa.
Can anyone advise how I can set up the test so that the request body does not contain these quotes?
Edit: I have also tried new ObjectContent(typeof(object)... and it gives the same result.
Another way is to bypass the MediaTypeFormatter by using StringContent instead of ObjectContent:
var content = new StringContent("aaaaa");
httpRequestMessage.Content = content;
Okay, so I needed to create a media type formatter that didn't interfere with the input in any way. I used this:
private class DoNothingTypeFormatter : MediaTypeFormatter
{
public override bool CanReadType(Type type)
{
return false;
}
public override bool CanWriteType(Type type)
{
if (type == typeof(string))
{
return true;
}
return false;
}
public override Task WriteToStreamAsync(Type type, object value, System.IO.Stream writeStream, HttpContent content, TransportContext transportContext)
{
var myString = value as string;
if (myString == null)
{
throw new Exception("Everything is supposed to be a string here.");
}
var length = myString.Length;
var bytes = System.Text.Encoding.UTF8.GetBytes(myString);
return Task.Factory.StartNew(() => writeStream.Write(bytes, 0, length));
}
}
Then, when I want to generate the body of the `HttpRequestMessage', I do so like this:
objectContent = new ObjectContent(typeof(string)
, "not json"
, new DoNothingTypeFormatter());