I am allowing deletion of attachments in the File Download control. If a user deletes an attachment and navigates away from the page (without saving), the attachment does not actually get removed.
There is an onclick event for the control, but it isn't specific to deletion. Is there a way to automatically call a .save() after deletion of an attachment?
Here is a SSJS snippet which allows to add an action to the delete function of a FileDownload control.
<xp:this.beforeRenderResponse>
<![CDATA[#{javascript:
/***
* adds an additional method to "delete action"
* of a UIFileDownload control
*
* #param UIFileDownload component
* #author Sven Hasselbach
* #category SSJS
* #category UI
* #version 0.3
*/
function overrideFileDownloadAction( fDownload ){
if( fDownload === null )
return;
rekOverrideFileDownloadAction( fDownload, fDownload );
}
function rekOverrideFileDownloadAction( component:javax.faces.component.UIOutput, fDownload:com.ibm.xsp.component.UIFileDownload ){
try{
var children:java.util.List = component.getChildren();
var it:java.util.Iterator = children.iterator();
var curChild:javax.faces.component.UIOutput;
while( it.hasNext() ){
curChild = it.next();
if( typeof( curChild ) === 'com.ibm.xsp.component.xp.XspEventHandler' ){
var group = new com.ibm.xsp.actions.ActionGroup();
var list = new java.util.ArrayList();
group.setComponent( fDownload );
list.add( curChild.getAction() );
list.add( mBinding );
group.setActions( list );
curChild.setAction(group);
}
rekOverrideFileDownloadAction( curChild , fDownload );
}
}catch(e){}
}
var mBinding = facesContext.getApplication().createMethodBinding("#{javascript:document1.save()}", null );
overrideFileDownloadAction( getComponent( 'fileDownload1' ) );
}]]>
</xp:this.beforeRenderResponse>
You have to change the code in the MethodBinding mBinding and the name of the FileDownLoad control. Please keep in mind that this code will only save the document if there are no validation problems. To disable required fields you have to add the following line of code curChild.setDisableValidators( true ); in the if block.
An alternative is to use the enableModifiedFlag property to ensure that the user is prompted if leaving the page without saving.
More details at http://publib.boulder.ibm.com/infocenter/domhelp/v8r0/topic/com.ibm.designer.domino.ui.doc/wpd_controls_pref_enablemodifiedflag.html
Related
I'm wondering if there is any simpler solution in sending specific order state emails.
Currently i use the following code to send e.g. the "order delivery shipped" email again.
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('orderNumber', $orderNumber));
$criteria->addAssociation('orderCustomer.salutation');
$criteria->addAssociation('orderCustomer.customer');
$criteria->addAssociation('stateMachineState');
$criteria->addAssociation('deliveries.shippingMethod');
$criteria->addAssociation('deliveries.shippingOrderAddress.country');
$criteria->addAssociation('deliveries.shippingOrderAddress.countryState');
$criteria->addAssociation('salesChannel');
$criteria->addAssociation('language.locale');
$criteria->addAssociation('transactions.paymentMethod');
$criteria->addAssociation('lineItems');
$criteria->addAssociation('currency');
$criteria->addAssociation('addresses.country');
$criteria->addAssociation('addresses.countryState');
/** #var OrderEntity|null $order */
$order = $this->orderRepository->search($criteria, $context)->first();
$context = new Context(new SystemSource());
$salesChannelContext = $this->orderConverter->assembleSalesChannelContext($order, $context)->getContext();
$salesChannelContext->addExtension(
MailSendSubscriber::MAIL_CONFIG_EXTENSION,
new MailSendSubscriberConfig(false)
);
$event = new OrderStateMachineStateChangeEvent(
'state_enter.order_delivery.state.shipped',
$order,
$salesChannelContext
);
$flowEvent = new FlowEvent('action.mail.send', new FlowState($event), [
'recipient' => [
'data' => [],
'type' => 'default',
],
'mailTemplateId' => $mailTemplateId,
]);
$this->sendMailAction->handle($flowEvent);
Given that you don't actually want to change the state, this looks like a good solution.
If you actually needed to programmatically change the state you could use the StateMachineRegistry service.
$transition = new Transition(OrderDeliveryDefinition::ENTITY_NAME, $orderDeliveryId, StateMachineTransitionActions::ACTION_SHIP, 'stateId');
$this->stateMachineRegistry->transition($transition, Context::createDefaultContext());
However if the current state is equal to the state you want to transition to, then an UnnecessaryTransitionException is expected to be thrown. You'd have to change the state back to a previous one before doing this and at that point your code is probably less trouble, if all you want to do is to repeat sending that mail.
Do you want to resend the mail or regenerate it? If you want to resend the mail you could use this out-of-the-box solution from FriendsOfShopware -> https://store.shopware.com/en/frosh97876597450f/mail-archive.html
You can build and send the mail with the Shopware\Core\Content\Mail\Service\MailService by yourself. But is way more code then just triggering it with a state change.
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('mailTemplateTypeId', self::ORDER_CONFIRMATION_MAIL_TYPE_ID));
$criteria->addAssociation('media.media');
$criteria->setLimit(1);
if ($order->getSalesChannelId()) {
$criteria->addFilter(new EqualsFilter('mail_template.salesChannels.salesChannel.id', $order->getSalesChannelId()));
/** #var MailTemplateEntity|null $mailTemplate */
$mailTemplate = $this->mailTemplateRepository->search($criteria, $this->context)->first();
// Fallback if no template for the saleschannel is found
if (null === $mailTemplate) {
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('mailTemplateTypeId', self::ORDER_CONFIRMATION_MAIL_TYPE_ID));
$criteria->addAssociation('media.media');
$criteria->setLimit(1);
/** #var MailTemplateEntity|null $mailTemplate */
$mailTemplate = $this->mailTemplateRepository->search($criteria, $this->context)->first();
}
} else {
/** #var MailTemplateEntity|null $mailTemplate */
$mailTemplate = $this->mailTemplateRepository->search($criteria, $this->context)->first();
}
if (null === $mailTemplate) {
return;
}
$data = new DataBag();
$data->set('recipients', [
$order->getOrderCustomer()->getEmail() => $order->getOrderCustomer()->getFirstName() . ' ' . $order->getOrderCustomer()->getLastName(),
]);
$data->set('senderName', $mailTemplate->getTranslation('senderName'));
$data->set('salesChannelId', $order->getSalesChannelId());
$data->set('templateId', $mailTemplate->getId());
$data->set('customFields', $mailTemplate->getCustomFields());
$data->set('contentHtml', $mailTemplate->getTranslation('contentHtml'));
$data->set('contentPlain', $mailTemplate->getTranslation('contentPlain'));
$data->set('subject', $mailTemplate->getTranslation('subject'));
$data->set('mediaIds', []);
$attachments = [];
if (null !== $mailTemplate->getMedia()) {
foreach ($mailTemplate->getMedia() as $mailTemplateMedia) {
if (null === $mailTemplateMedia->getMedia()) {
continue;
}
if (null !== $mailTemplateMedia->getLanguageId() && $mailTemplateMedia->getLanguageId() !== $order->getLanguageId()) {
continue;
}
$attachments[] = $this->mediaService->getAttachment(
$mailTemplateMedia->getMedia(),
$this->context
);
}
}
if (!empty($attachments)) {
$data->set('binAttachments', $attachments);
}
try {
if (
null === $this->mailService->send(
$data->all(),
$this->context,
$this->getTemplateData($order)
)
) {
throw new \Exception('Could not render mail template');
}
$this->orderMailRepository->upsert([[
'orderId' => $order->getId(),
'confirmationSend' => true,
]], $this->context);
} catch (\Exception $e) {
$this->logger->error(sprintf(
'Could not send mail for order %s: %s',
$order->getOrderNumber(),
$e->getMessage()
));
}
}
I have created a model and added the $has_many for selecting multiple products. This is working fine but I am unable to make the selected products sortable by drag and drop. I know this is possible I have seen it. But I am unable to find anything in the documentation that shows how to get this done. Here is my model:
<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
require_once(FUEL_PATH.'models/Base_module_model.php');
/**
* This model handles setting up the form fields for our contact form
*
*/
class Products_category_model extends Base_module_model {
public $required = array('name', 'published');
public $has_many = array('products' => array(FUEL_FOLDER => 'Products_model'));
function __construct()
{
parent::__construct('w_product_categories');
}
/*
This will provide the list of records for display in the Admin Panel
Note how the "excerpt" will display, but truncated
Because we are using some MySQL functions in our select statement,
we pass FALSE in as the second parament to prevent CI auto escaping of fields
*/
function list_items($limit = null, $offset = null, $col = 'name', $order = 'asc', $just_count = false)
{
$this->db->select('id, name, published', FALSE);
$data = parent::list_items($limit, $offset, $col, $order);
return $data;
}
function form_fields($values = array(), $related = array())
{
$fields = parent::form_fields($values, $related);
return $fields;
}
}
class Product_category_model extends Base_module_record {
}
So it is very simple I discovered. I added this in the form fields function:
// Makes the has many drag and drop sortable.
$fields['products']['sorting'] = TRUE;
$fields['products']['after_html'] = "<div style=\"clear:both;font-style:italic\">NOTE: you can sort selected product to your choosing by clicking on the product and then dragging it into the desired placement in the list</div>";
I have :
a 3 level menu
custom records from a table called
"tx_products_domain_model_product"
products have a field called "url_alias"
On any page on that menu, I can have a product record.
For pages that have a product record I want the link to look like:
http://www.sitedomain.com/<url_alias>
Can this be done with typoscript?
EDIT
It looks like it's too complex to do all this with typoscript, so I'm using a userFunc. I'm checking if there is a product record with a shop ID and want to return that shop id. If not, return the current menu page UID. The problem is, how can I pass the menu page UID as parameter to the userFunc?
class user_productsOnCurrentPage
{
function main( $content, $conf )
{
if ( TYPO3_MODE !== 'FE' )
{
return FALSE;
}
$product = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow(
'shop_id', 'tx_products_domain_model_product', 'pid=' . $conf['currentPageId'] . ' AND deleted=0 AND hidden=0' );
if ( is_array( $product ) && ! empty( $product ['shop_id'] ) )
{
return $product ['shop_id'];
}
return $conf['currentPageId'];
}
}
The menu:
lib.mainMenu = HMENU
lib.mainMenu {
...
1 = TMENU
1 {
...
NO = 1
NO {
...
# show direct url for external links
doNotLinkIt = 1
stdWrap.cObject = CASE
stdWrap.cObject {
key.field = doktype
default = TEXT
default {
field = nav_title // title
typolink.parameter.cObject = USER
typolink.parameter.cObject {
userFunc = user_productsOnCurrentPage->main
currentPageId = ?????
}
typolink.wrap = |<span><strong></strong></span>
typolink.ATagBeforeWrap = 1
stdWrap.htmlSpecialChars = 1
}
...
I could access the page ID directly in the user function:
TypoScript:
typolink.userFunc = user_mcfazabosOnCurrentPage->main
PHP:
$pageId = $this->cObj->getFieldVal( 'uid' );
I have a form build from the kohana framework which should send an email.
when I press the "send" button, everything seems to work fine...no error messages...but no email turns up!
If I can´t see anything in firebug..where can I look?
the application logs state
"error: Missing i18n entry contact.captcha.valid for language en_US"
but I don´t know how to get to the bottom of the problem..any help welcome..
yours,
Rob
Ill try and find out which version im using.....the application is the latest version of Ushahidi (2.2.1) www.ushahidi.com
<?php defined('SYSPATH') OR die('No direct access allowed.');
/**
* Captcha library.
*
* $Id: Captcha.php 3917 2009-01-21 03:06:22Z zombor $
*
* #package Captcha
* #author Kohana Team
* #copyright (c) 2007-2008 Kohana Team
* #license http://kohanaphp.com/license.html
*/
class Captcha_Core {
// Captcha singleton
protected static $instance;
// Style-dependent Captcha driver
protected $driver;
// Config values
public static $config = array
(
'style' => 'basic',
'width' => 150,
'height' => 50,
'complexity' => 4,
'background' => '',
'fontpath' => '',
'fonts' => array(),
'promote' => FALSE,
);
/**
* Singleton instance of Captcha.
*
* #return object
*/
public static function instance()
{
// Create the instance if it does not exist
empty(self::$instance) and new Captcha;
return self::$instance;
}
/**
* Constructs and returns a new Captcha object.
*
* #param string config group name
* #return object
*/
public static function factory($group = NULL)
{
return new Captcha($group);
}
/**
* Constructs a new Captcha object.
*
* #throws Kohana_Exception
* #param string config group name
* #return void
*/
public function __construct($group = NULL)
{
// Create a singleton instance once
empty(self::$instance) and self::$instance = $this;
// No config group name given
if ( ! is_string($group))
{
$group = 'default';
}
// Load and validate config group
if ( ! is_array($config = Kohana::config('captcha.'.$group)))
throw new Kohana_Exception('captcha.undefined_group', $group);
// All captcha config groups inherit default config group
if ($group !== 'default')
{
// Load and validate default config group
if ( ! is_array($default = Kohana::config('captcha.default')))
throw new Kohana_Exception('captcha.undefined_group', 'default');
// Merge config group with default config group
$config += $default;
}
// Assign config values to the object
foreach ($config as $key => $value)
{
if (array_key_exists($key, self::$config))
{
self::$config[$key] = $value;
}
}
// Store the config group name as well, so the drivers can access it
self::$config['group'] = $group;
// If using a background image, check if it exists
if ( ! empty($config['background']))
{
self::$config['background'] = str_replace('\\', '/', realpath($config['background']));
if ( ! is_file(self::$config['background']))
throw new Kohana_Exception('captcha.file_not_found', self::$config['background']);
}
// If using any fonts, check if they exist
if ( ! empty($config['fonts']))
{
self::$config['fontpath'] = str_replace('\\', '/', realpath($config['fontpath'])).'/';
foreach ($config['fonts'] as $font)
{
if ( ! is_file(self::$config['fontpath'].$font))
throw new Kohana_Exception('captcha.file_not_found', self::$config['fontpath'].$font);
}
}
// Set driver name
$driver = 'Captcha_'.ucfirst($config['style']).'_Driver';
// Load the driver
if ( ! Kohana::auto_load($driver))
throw new Kohana_Exception('core.driver_not_found', $config['style'], get_class($this));
// Initialize the driver
$this->driver = new $driver;
// Validate the driver
if ( ! ($this->driver instanceof Captcha_Driver))
throw new Kohana_Exception('core.driver_implements', $config['style'], get_class($this), 'Captcha_Driver');
Kohana::log('debug', 'Captcha Library initialized');
}
/**
* Validates a Captcha response and updates response counter.
*
* #param string captcha response
* #return boolean
*/
public static function valid($response)
{
// Maximum one count per page load
static $counted;
// User has been promoted, always TRUE and don't count anymore
if (self::instance()->promoted())
return TRUE;
// Challenge result
$result = (bool) self::instance()->driver->valid($response);
// Increment response counter
if ($counted !== TRUE)
{
$counted = TRUE;
// Valid response
if ($result === TRUE)
{
self::instance()->valid_count(Session::instance()->get('captcha_valid_count') + 1);
}
// Invalid response
else
{
self::instance()->invalid_count(Session::instance()->get('captcha_invalid_count') + 1);
}
}
return $result;
}
/**
* Gets or sets the number of valid Captcha responses for this session.
*
* #param integer new counter value
* #param boolean trigger invalid counter (for internal use only)
* #return integer counter value
*/
public function valid_count($new_count = NULL, $invalid = FALSE)
{
// Pick the right session to use
$session = ($invalid === TRUE) ? 'captcha_invalid_count' : 'captcha_valid_count';
// Update counter
if ($new_count !== NULL)
{
$new_count = (int) $new_count;
// Reset counter = delete session
if ($new_count < 1)
{
Session::instance()->delete($session);
}
// Set counter to new value
else
{
Session::instance()->set($session, (int) $new_count);
}
// Return new count
return (int) $new_count;
}
// Return current count
return (int) Session::instance()->get($session);
}
/**
* Gets or sets the number of invalid Captcha responses for this session.
*
* #param integer new counter value
* #return integer counter value
*/
public function invalid_count($new_count = NULL)
{
return $this->valid_count($new_count, TRUE);
}
/**
* Resets the Captcha response counters and removes the count sessions.
*
* #return void
*/
public function reset_count()
{
$this->valid_count(0);
$this->valid_count(0, TRUE);
}
/**
* Checks whether user has been promoted after having given enough valid responses.
*
* #param integer valid response count threshold
* #return boolean
*/
public function promoted($threshold = NULL)
{
// Promotion has been disabled
if (self::$config['promote'] === FALSE)
return FALSE;
// Use the config threshold
if ($threshold === NULL)
{
$threshold = self::$config['promote'];
}
// Compare the valid response count to the threshold
return ($this->valid_count() >= $threshold);
}
/**
* Returns or outputs the Captcha challenge.
*
* #param boolean TRUE to output html, e.g. <img src="#" />
* #return mixed html string or void
*/
public function render($html = TRUE)
{
return $this->driver->render($html);
}
/**
* Magically outputs the Captcha challenge.
*
* #return mixed
*/
public function __toString()
{
return $this->render();
}
} // End Captcha Class
From the little details provided I think your site uses the I18n library from Kohana for internationalization of the site. The other option is your site use Kohana messages for showing form errors.
The Validation class uses the __() function internally when generation validation errors. I think you don't have a message for key valid specified in APPPATH/messages/contact/captcha.php.
You should try to investigate more on how the form is processed and whether some validation errors are being generated. There might be an error in the captcha validation and despite it seem there is no error, there might be and just the error message could not be shown.
class Contact_Controller extends Main_Controller
{
function __construct()
{
parent::__construct();
}
public function index()
{
$this->template->header->this_page = 'contact';
$this->template->content = new View('contact');
$this->template->header->page_title .= Kohana::lang('ui_main.contact').Kohana::config('settings.title_delimiter');
// Setup and initialize form field names
$form = array (
'contact_name' => '',
'contact_email' => '',
'contact_phone' => '',
'contact_subject' => '',
'contact_message' => '',
'captcha' => ''
);
// Copy the form as errors, so the errors will be stored with keys
// corresponding to the form field names
$captcha = Captcha::factory();
$errors = $form;
$form_error = FALSE;
$form_sent = FALSE;
// Check, has the form been submitted, if so, setup validation
if ($_POST)
{
// Instantiate Validation, use $post, so we don't overwrite $_POST fields with our own things
$post = Validation::factory($_POST);
// Add some filters
$post->pre_filter('trim', TRUE);
// Add some rules, the input field, followed by a list of checks, carried out in order
$post->add_rules('contact_name', 'required', 'length[3,100]');
$post->add_rules('contact_email', 'required','email', 'length[4,100]');
$post->add_rules('contact_subject', 'required', 'length[3,100]');
$post->add_rules('contact_message', 'required');
$post->add_rules('captcha', 'required', 'Captcha::valid');
// Test to see if things passed the rule checks
if ($post->validate())
{
// Yes! everything is valid - Send email
$site_email = Kohana::config('settings.site_email');
$message = Kohana::lang('ui_admin.sender').": " . $post->contact_name . "\n";
$message .= Kohana::lang('ui_admin.email').": " . $post->contact_email . "\n";
$message .= Kohana::lang('ui_admin.phone').": " . $post->contact_phone . "\n\n";
$message .= Kohana::lang('ui_admin.message').": \n" . $post->contact_message . "\n\n\n";
$message .= "~~~~~~~~~~~~~~~~~~~~~~\n";
$message .= Kohana::lang('ui_admin.sent_from_website'). url::base();
// Send Admin Message
email::send( $site_email, $post->contact_email, $post->contact_subject, $message, FALSE );
$form_sent = TRUE;
}
// No! We have validation errors, we need to show the form again, with the errors
else
{
// repopulate the form fields
$form = arr::overwrite($form, $post->as_array());
// populate the error fields, if any
$errors = arr::overwrite($errors, $post->errors('contact'));
$form_error = TRUE;
}
}
$this->template->content->form = $form;
$this->template->content->errors = $errors;
$this->template->content->form_error = $form_error;
$this->template->content->form_sent = $form_sent;
$this->template->content->captcha = $captcha;
// Rebuild Header Block
$this->template->header->header_block = $this->themes->header_block();
$this->template->footer->footer_block = $this->themes->footer_block();
}
}
My problem has been solved!
My Email server was not set up properly. I resolved the postfix issue and now the comments form is working as it should,
thanks to all who answered,
Rob
I have a custom module created in Drupal 7 and I want it to display some HTML content. Here is how I have did.
But it is not working, what I do wrong?
<?php
/**
* Implements hook_block_info().
*/
function submenus_block_info() {
$blocks = array();
$blocks['info'] = array(
'info' => t('The submenu zone')
);
return $blocks;
}
/**
* Implements hook_block_view().
*
*/
function submenus_block_view($delta = '') {
$block = array();
$users = "edf";
$title = "sdfsd";
$block['subject'] = t('Submenu');
$block['content'] = theme('submenus_output', array('users' => $users, 'title' => $title));
return $block;
}
/**
* Implement hook_theme()
*/
function submenus_theme() {
return array(
'submenus_output' => array(
'variables' => array('users' => NULL, 'title' => NULL),
),
);
}
/**
* Display output
*/
function theme_submenus_output($somearray) {
$content = '<div>TEST</div>';
return $content;
}
?>
I checked, there is nothing wrong with that code: the new block is available in the list of blocks, and if you assign it to a region, the block is called and the code from the custom theme function is displayed.
So you could try these things:
in Administration > Configuration > Development > Performance, clear the caches
in Administration > Structure > Blocks, make sure the block is assigned to a region that exists (such as "Content") and if it is, click the "Configure" link to see if there is a filter that prevents it from being displayed.