I use JFactory::getApplication()->enqueueMessage('Message goes here', 'error') to show users the request could not be processed, it works OK but Joomla orders the messages in the sequence they occur. Because my message happens before the Joomla save error is captured, the user sees this message:
you cannot do this operation //my message
Save failed with the following error: //Joomla message
I want to invert the order and have Joomla message as it is, followed by my message so that it makes sense:
Save failed with the following error: // Joomla message
you cannot do this operation // my message
Is that possible? (without language translation or overrides?)
After help from answers, I could do the inversion: 1st message is a placeholder to be searched using getMessageQueue(). Although you could delete messages in J.2.5 it is no longer possible with J.3+ (https://developer.joomla.org/joomlacode-archive/issue-33270.html). The solution is to reflect the class to unprotect the queue and replace it.
public static function reorderMessages()
{
//error messages
$err01 = JText::_('COM_COMPONENT_MESSAGE1');
//you can adapt and add other messages here
$app = JFactory::getApplication();
$new_messages = array();
$replacement_found = null;
//mirror protected $_messageQueue
$appReflection = new ReflectionClass(get_class($app));
$_messageQueue = $appReflection->getProperty('_messageQueue');
$_messageQueue->setAccessible(true);
//get messages
$messages = $app->getMessageQueue();
foreach($messages as $key=>$message)
{
if($messages[$key]['message'] == 'MESSAGE_TO_REPLACE' && $messages[$key]['type'] == 'error' )
{
$replacement_found = 1;
continue;
}
$new_messages[] = $message;
}
if($replacement_found)
{
//save all messages
$_messageQueue->setValue($app, $new_messages);
//add replacement message to the end of the queue
$app->enqueueMessage(JText::_($err01, 'error');
}
return true;
}
Be very careful where to call the function, if the message queue is empty Joomla will return an error and break your code. Make sure you have enqueued the 1st message before calling the function.
You can use getMessageQueue() on the application object (i.e., $myApp = JFactory::getApplication()) to get a copy of the message queue array. You can clear the message queue by passing true to the getMessageQueue()` function call. It will still return a copy of the system message queue array.
You could then use regex's to find the keys in the array and reorder them. I would find the system error message in the translation file, and use the error message key from the translation .ini file (instead of the actual text of the error message) for the regex search so it doesn't break if the error message changes. I'd also do it in a plugin and on a late lifecycle hook (maybe the onBeforeRender event).
You can save the modified message queue array back to the JApplication class instance using the application object's enqueueMessage() method, which has this signature:
enqueueMessage(string $msg, string $type = 'message') : void
source
Related
What I'm trying to do here is to implement a callback function in a Laravel 5.4 controller. This uses Authorize.net to process a credit card transaction, then inserts a bunch of stuff into the database, sends some messages, makes an invoice and airbill, and so on.
What I WANT to happen is:
Hit the "Submit" button, sends AJAX request
Processes the Authorize.net transaction
If good, then call a callback function to do all the gruntwork but return a transaction response.
4) Notify the user
The reason I wanna do it this way is that I want the user to wait the minimum amount of time to see the result of their payment processing without having to wait another 5 seconds or so staring at a spinning wheel waiting to go to the order complete page.
Can a callback function help me do this?
Thanks
My current implementation results in a 500 error, and I'm not quite sure what I should do from here...
[ route in web.config ]
// AJAX call to process the transaction, insert the new order, inform the user of success/failure
Route::post('/shop/processtransaction', 'OrderCheckoutController#processTransaction');
[ function processTransaction in OrderCheckoutController.php ]
public function processTransaction(Request $request) {
return self::processPaymentAndOrderInsertion($request, 'createOrder');
}
[ function processPaymentAndOrderInsertion in OrderCheckoutController.php ]
public function processPaymentAndOrderInsertion(Request $request, callable $createOrderCallback = null) {
$order_proc = new OrderProcessingTools;
$transaction_response = $order_proc->processTransaction($request);
if($transaction_response['success'] === true) {
self::$createOrderCallback($request, $transaction_response);
}
return json_encode($transaction_response);
}
[ my callback function ]
public function createOrder(Request $request, $transaction_response) {
$order_proc = new OrderProcessingTools;
$new_order = $order_proc->insertNewOrder($request);
$new_order->payment_status_id = $transaction_response['response_data']['order_payment_status_id'];
$new_order->save();
// record the payment transaction
$order_proc->insertOrderPaymentData($new_order, $transaction_response);
// insert the travelers for this order
$travelers = $order_proc->insertOrderTravelers($new_order);
// insert order inbound shipment record
$order_proc->insertInboundOrderShipping($new_order->id);
// generate inbound shipping airbill
$order_proc->generateInboundShippingAirbill($new_order->id);
/// generate the invoive
$order_proc->generateInvoice($new_order);
// send new order notification to the user
$order_proc->sendNewOrderNotificationToUser($new_order);
// send new order notification to admin
$order_proc->sendNewOrderNotificationToAdmin($new_order);
// finally kill the session variable
$_SESSION['travelers'] = [];
}
[ my previous non-asynchronous implementation looks like this...]
public function processTransaction(Request $request) {
// :: POST
// Process the Authorize.net transaction, insert the order, generate invoices
// and airbills, send notifications
$order_proc = new OrderProcessingTools;
$transaction_response = $order_proc->processTransaction($request);
if($transaction_response['success'] === true) {
// insert a new order
$new_order = $order_proc->insertNewOrder($request);
$new_order->payment_status_id = $transaction_response['response_data']['order_payment_status_id'];
$new_order->save();
// record the payment transaction
$order_proc->insertOrderPaymentData($new_order, $transaction_response);
// insert the travelers for this order
$travelers = $order_proc->insertOrderTravelers($new_order);
// insert order inbound shipment record
$order_proc->insertInboundOrderShipping($new_order->id);
// generate inbound shipping airbill
$order_proc->generateInboundShippingAirbill($new_order->id);
/// generate the invoive
$order_proc->generateInvoice($new_order);
// send new order notification to the user
$order_proc->sendNewOrderNotificationToUser($new_order);
// send new order notification to admin
$order_proc->sendNewOrderNotificationToAdmin($new_order);
// finally kill the session variable
$_SESSION['travelers'] = [];
}
// either good news or bad news at this point..
return json_encode($transaction_response);
}
When I try it this way, this is the error that is returned...
xception: "Symfony\Component\Debug\Exception\FatalThrowableError"
file: "F:\wamp64\www\uspassports\public_html\app\Http\Controllers\OrderCheckoutController.php"
line: 105
message: "Argument 2 passed to App\Http\Controllers\OrderCheckoutController::processPaymentAndOrderInsertion() must be callable or null, string given
You need to pass a callable type, but passing just the string name of the method won't work as PHP will only check if it's a global function.
You need to pass an array, with the first parameter being the object to call the method on, and the second the name of the function, like so:
return self::processPaymentAndOrderInsertion($request, [$this, 'createOrder']);
Documentation: https://www.php.net/manual/en/language.types.callable.php
I am building an entity listener in an application based on symfony 2.7. It will apply a tag to a video whenever the video's owning group changes.
My listener looks like this so far:
public function preUpdate($video, $args)
{
$changeSet = $args->getEntityChangeSet();
if(!array_key_exists('ownerGroup', $changeSet )){
return;
}
$oldGroupObj = $changeSet['ownerGroup'][0];
$oldGroupName = $oldGroupObj->getName();
//die($oldGroupName); //Gives us a valid group name string.
$tagRepository = $args->getEntityManager()->getRepository('AppBundle:Tag');
$tag = $tagRepository->findOneBy( ['title' => 'migrated' . $oldGroupName] );
if( $tag === null ){
$tag = new Tag;
$tag->setTitle('migrated' . $oldGroupName);
}
$video->addTag($tag);
}
The problem is that last line. When I run it, it causes this exception:
The given model has already started the "default_workflow" process.
What does this exception mean, and how can I save the new tag to my video when the owning group changes?
Trully, I have not develop application with symfony. But after read some documentation symfony, for your case exception The given model has already started the "default_workflow" process, interested at "Process Component", focused at Running Function (mustRun()), except that it will throw a ProcessFailedException if the process couldn't be executed successfully.
So, Globally, do not just focussed at your "function preUpdate", but all-of-your-big-code-symfony, because that exception "open from" The Process-Of-Your-Application
I'm looking at both my code and my result and I don't see any glaring error, so I thought it could be useful to have a few extra sets of eyes look it over.
I have a custom PayPal IPN listener that updates a transaction table in the database. I deployed it Friday before leaving work, and after returning today it seems to be working mostly correctly; but I would like to figure out why one insert behaved strangely.
Here's a capture of the inserts which happened over the weekend:
As you can see, the intended JSON value for the log column of the 4th transaction is empty. I find it strange because the value of the transaction_id column is being parsed from the same array.
Here is the relevant db insert code:
// Generate valid IPN log
private function generateIpnLog () {
global $wpdb;
// prepare log
$array_log = [];
$array_log['verified'] = true;
$array_log['ipn_response'] = (isset($this->PayPal_Response)) ? : 'Error reading from POST array';
// Parse transaction ID
$transaction_id = (isset($this->PayPal_Response['txn_id'])) ? $this->PayPal_Response['txn_id'] : null;
// Generate log
$log = json_encode($array_log);
// Update DB
$wpdb->insert(
'log_paypal',
[
'transaction_id' => ($transaction_id) ? $transaction_id : 'Error getting transaction ID',
'log' => ($log) ? $log : 'Error generating transaction log'
],
[
'%s',
'%s'
]
);
// json log response
$this->json_return = $log;
}
Seeing as the transaction id is parsed fine from the PayPal response, and because we know $array_log['verified'] has an explictly declared value my guess is there must be a problem with json_encode($array_log).
I also checked the data from PayPal IPN history of the PayPal account in question, and can verify there isn't anything different about the way the data is being formed in the null log column vs the others.
Anyone have an idea about what could be happening in this instance?
As suggested by #ishegg it was an encoding issue since PayPal IPN uses windows-1252 encoding and the DB field was encoded in UTF-8.
It was easy to fix in this case since the PayPal return data is not nested / multidimensional (see below).
Called in an earlier function after a PayPal IPN entry is cryptographically verified by the cert chain:
// Process IPN response
$this->PayPal_Response = $this->processIpn();
Then, the function itself:
// Manipulate IPN response and prepare
// for database update and log
private function processIpn () {
// Response ref.
$response = $this->post_array;
if ( isset($response['charset']) ) {
if ($response['charset'] == "windows-1252") {
foreach ($response as $key => $ipn_value) {
$response[$key] = mb_convert_encoding($ipn_value, 'UTF-8', 'Windows-1252');
}
}
}
return $response;
}
While using Google Cloud Compute's API in PHP, I am able to start, stop, delete instances as well as create and delete disks.
However, when trying to create an Instance, I keep getting this error
Invalid value for field 'resource.disks'
PHP Fatal error: Uncaught exception 'Google_Service_Exception' with message 'Error calling POST https://www.googleapis.com/compute/v1/projects/project/zones/zone/instances: (400) Invalid value for field 'resource.disks': ''. Boot disk must be specified.' in /var/www/html/google/google-api-php-client/src/Google/Http/REST
Here is the request I am making
self::connectClient();
$computeService = new Google_Service_Compute($this->client);
if ($this->client->getAccessToken())
{
$googleNetworkInterfaceObj = new Google_Service_Compute_NetworkInterface();
$network = self::DEFAULT_NETWORK;
$googleNetworkInterfaceObj->setNetwork($network);
$diskObj = self::getDisk($instance_name);
$new_instance = new Google_Service_Compute_Instance();
$new_instance->setName($instance_name);
$new_instance->setMachineType(self::DEFAULT_MACHINE_TYPE);
$new_instance->setNetworkInterfaces(array($googleNetworkInterfaceObj));
$new_instance->setDisks(array(
"source"=>$diskObj->selfLink,
"boot"=>true,
"type"=>"PERSISTENT",
"deviceName"=>$diskObj->name,
));
$insertInstance = $computeService->instances->insert(self::DEFAULT_PROJECT,self::DEFAULT_ZONE_NAME, $new_instance);
Any help will be highly appreciated, thank you.
Ok the solution was really simple (and silly)
Instead of
$new_instance->setDisks(array(
"source"=>$diskObj->selfLink,
"boot"=>true,
"type"=>"PERSISTENT",
"deviceName"=>$diskObj->name,
));
It's supposed to be
$new_instance->setDisks(array(
array(
'source'=>self::getDisk($instance_name)->selfLink,
'boot'=>true,
'type' => "PERSISTENT",
'deviceName'=>self::getDisk($instance_name)->name,
)
));
I have two questions related to the FlashMessenger view helper. Both questions are to do this code:
My action method:
private $_messages; // set to $this->_helper->FlashMessenger; in init()
public function loginAction() {
// > login validation <
// Switch based on the result code
switch ($result->getCode()) {
// > snip several cases <
case Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID:
$this->_messages->addMessage("That wasn't the right password.");
break;
case Zend_Auth_Result::SUCCESS:
$this->_messages->addMessage('Logged you in successfully. Welcome back!');
$this->_helper->Redirector('index', 'home');
break;
}
// >snip<
$this->view->messages = $this->_messages->getMessages();
$this->render();
}
My layout (Zend_Layout) view script:
<?php if (isset($this->messages) && count($this->messages) > 0) {
print_r($this->messages);
//$this->partialLoop('partials/messages.phtml', $this->messages);
} ?>
Why is the message not output the first time it is set?
I have a feeling this is to do with the messenger being stored in sessions but I'm sure it's to do with my implementation.
When I submit a bad value to my form I don't get a message until I either send the form again or refresh.
What is a good way of sending this to the PartialLoop helper?
The output of the messenger is something like:
Array(
[0] => 'Message',
[1] => 'Second message' //etc.
)
But this is no good for a PartialLoop as I need to get the message (which needs each message to be an array, containing a 'message' => 'Message string' key/value pair).
Is there a better method instead of rewriting the array before submitting it to the view?
Referring to OIS I'd like to add that you can retrieve the flash-messages within the same request in which they where added to the FlashMessenger. In this case you'd have to use Zend_Controller_Action_Helper_FlashMessenger::getCurrentMessages().
In your case you'd have to change the line
$this->view->messages = $this->_messages->getMessages();
to
$this->view->messages = $this->_messages->getCurrentMessages();
Hope that helps.
Ill quote from the documentation.
10.8.4.4.1. Introduction
The FlashMessenger helper allows you
to pass messages that the user may
need to see on the next request. To
accomplish this, FlashMessenger uses
Zend_Session_Namespace to store
messages for future or next request
retrieval.
Basically, this means that yes you have to refresh the page to see the messages.
Edit for PartialLoop:
You could try this:
foreach ($array as $message) {
$newArray[]['message'] = $message;
}
But you dont have to use PartialLoop. You could send it to Partial and loop it there. Or even loop it right there in your view.