add language constants to Joomla component javascript - joomla

My component includes a java script file:
$doc->addScript("/components/com_cam/js/cam.js");
I have several client side messages that I'd like to add with language constants, i.e.
<?php echo JText::_('COM_CAM_SEND_LABEL'); ?>
Easy enough in your front end php code like default.php but what about messages inside cam.js?
Such as my jquery validation:
messages: {
cam: {
required: "Enter a label",
minlength: jQuery.format("At least {0} characters required!"),
maxlength: jQuery.format("Maximum {0} characters allowed!")
}
}
What is the best practice for this?

In Joomla! 2.5 (since 1.6 I believe) there is JText::script() which adds support for adding language keys to a global array() so that your Javascript can access them.
First up, in your PHP you can call JText::script('COM_MYCOMPONENT_MSG1'); for each string you need translated in your Javascript.
The you can use the built-in Joomla.JText._('COM_MYCOMPONENT_MSG1') in your Javascript to retrieve it.
When you get to the point where there a lots of strings to be converted you may find it easier to just parse the javascript file at run time (in-efficient yada yada but for back-end admin screens not such a big deal).
/**
* Parses a javascript file looking for JText keys and then loads them ready for use.
*
* #param string $jsFile Path to the javascript file.
*
* #return bool
*/
public static function loadJSLanguageKeys($jsFile)
{
if (isset($jsFile))
{
$jsFile = JPATH_SITE . $jsFile;
}
else
{
return false;
}
if ($jsContents = file_get_contents($jsFile))
{
$languageKeys = array();
preg_match_all('/Joomla\.JText\._\(\'(.*?)\'\)\)?/', $jsContents, $languageKeys);
$languageKeys = $languageKeys[1];
foreach ($languageKeys as $lkey)
{
JText::script($lkey);
}
}
}

Make a helper function to build validation messages and add it to the head.
Something like bellow, just edit it to suit your needs
$messages = '(function ($) {
$.extend($.validator.messages, {
cam: {
required: "' . JText::_('COM_CAM_VALIDATION_REQUIRED') . '",
minlength: jQuery.format("' . JText::_('COM_CAM_VALIDATION_MINIMUM') . '"),
maxlength: jQuery.format("' . JText::_('COM_CAM_VALIDATION_MAXIMUM') . '")
}
});
}(jQuery));';
$doc = JFactory::getDocument();
$doc->addScriptDeclaration($messages);

Related

cy.findByText - string substitution

delete(customData: Data): void {
const { name } = customData;
cy.findByText("Successfully removed `${name}`!", {timeout : 15000});
I'm not able to get this to work. Perhaps findByText() doesn't support this .
Is there another way I could do this ? The following message is an alert message and disappears in a few seconds after it's displayed. So, I'm unable to find the element locator for this alert message.
"Successfully removed name!"
You can use cy.contains() as an alternative to cy.findByText.
cy.contains(`Successfully removed ${name}!`, {timeout : 15000});
Or, If you want to use findByText you have to use this:
cy.findByText(`Successfully removed ${name}!`, {timeout : 15000});
You can learn more about Template literals from here.
You could also use this function:
function followLabel(label) {
return cy.contains('label', label)
.invoke('attr', 'for')
.then(id => cy.get('#' + id));
}
-> cy.followLable("Sucessfully removed");

Dynamic localization in vue-i18n

I would like to update my localization messages in vue-i18n dynamically.
I am building a webshop, where every item has descriptions in more languages. So what I’d like to achieve is when I get the webshop items from the REST API I want to put their names, descriptions etc. to the messages object in vue-i18n so it can work with them. Does the vue-i18n API have something to handle that? Also I am getting the data from the server (I get a Promise), so how can I make sure it gets updated in the browser view, when I finally get the response, and add the data to the localization?
What I did was write a mixin, and use it everywhere I need dynamic localization:
export default {
methods: {
$t: function (translate) {
if (typeof translate === 'string') {
return this.$i18n.t(translate)
} else if (translate === void 0) {
return this.$i18n.t('loading')
}
return translate[this.$i18n.locale]
}
}
}
This way when my texts look like the following, I can call $t(text) (NOT $t('text') of course):
data: function () {
return {text: {en:'Book', de:'Buch', hu:'Könyv'}}
}
So in your components you have to import this and add it as a mixin:
import dynamicLocalization from '#/components/mixins/dynamic-localization'
export default {
...
mixins:[dynamicLocalization]
...
}

How to make Behat wait for Angular ajax calls?

I have a reporting page that is basically a table you can add and remove columns from. When you add a column, the data for that column is fetched and loaded with ajax, using angular.
Consider this Behat scenario:
Given I have a user named "Dillinger Four"
And I am on "/reports"
When I add the "User's Name" column
Then I should see "Dillinger Four"
How can I make Behat wait until angular's ajax call completes? I would like to avoid using a sleep, since sleeps add unnecessary delay and will fail if the call takes too long.
I used the following to wait for jquery code:
$this->getSession()->wait($duration, '(0 === jQuery.active)');
I haven't found a similar value to check with angular.
Your link above was helpful, just to expand on it and save someone else a little time.
/**
* #Then /^I should see "([^"]*)" if I wait "([^"]*)"$/
*/
public function iShouldSeeIfIWait($text, $time)
{
$this->spin(function($context) use ($text) {
$this->assertPageContainsText($text);
return true;
}, intval($time) );
}
/**
* Special function to wait until angular has rendered the page fully, it will keep trying until either
* the condition is meet or the time runs out.
*
* #param function $lambda A anonymous function
* #param integer $wait Wait this length of time
*/
public function spin ($lambda, $wait = 60)
{
for ($i = 0; $i < $wait; $i++)
{
try {
if ($lambda($this)) {
return true;
}
} catch (Exception $e) {
// do nothing
}
sleep(1);
}
$backtrace = debug_backtrace();
throw new Exception(
"Timeout thrown by " . $backtrace[1]['class'] . "::" . $backtrace[1]['function'] . "()\n" .
$backtrace[1]['file'] . ", line " . $backtrace[1]['line']
);
}
Then in your Scenario use:
Then I should see "Something on the page." if I wait "5"
You can use code from Angular's Protractor library to wait for loading. Here you can find a function waitForAngular(). It simply waits for a client-side function with the same name
Here's working PHP code.
class WebContext implements Context
{
/**
* #Then the list of products should be:
*/
public function theListOfProductsShouldBe(TableNode $table)
{
$this->waitForAngular();
// ...
}
private function waitForAngular()
{
// Wait for angular to load
$this->getSession()->wait(1000, "typeof angular != 'undefined'");
// Wait for angular to be testable
$this->getPage()->evaluateScript(
'angular.getTestability(document.body).whenStable(function() {
window.__testable = true;
})'
);
$this->getSession()->wait(1000, 'window.__testable == true');
}
}

Magento: ImageCdn bug? (long story)

I have some question related with Magento's free extension OnePica ImageCdn.
A broken image appear in frontend when I uploaded "corrupt image".
Ok, let's start the long story:
I notice it is happened because of ImageCdn extension and "corrupt image".
In some part of ImageCdn's code:
OnePica_ImageCdn_Helper_Image
/**
* In older versions of Magento (<1.1.3) this method was used to get an image URL.
* However, 1.1.3 now uses the getUrl() method in the product > image model. This code
* was added for backwards compatibility.
*
* #return string
*/
public function __toString()
{
parent::__toString();
return $this->_getModel()->getUrl();
}
My question is, anybody know what is the purpose of that code?
I don't understand what is the meaning of their comment above.
I think it is a bug as it always return $this->_getModel()->getUrl();
Is is really a bug or it is just my wrong interpretation?
This is what I've done so far:
I have an image dummy.jpeg
After some investigation, I just realized that is a "corrupt image".
I tested using: <?php print_r(getimagesize('dummy.jpeg')); ?>
Result:
Array
(
[0] => 200
[1] => 200
[2] => 6
[3] => width="200" height="200"
[bits] => 24
[mime] => image/x-ms-bmp
)
Of course I was surprised by the result because it looks good when I open it using Preview (on Mac OSX)
Then I open it using hex editor, the first two bytes is : BM which is BMP's identifier
I tried to upload .bmp image for product -> failed, can not select the image
I asked my colleague to upload it too (on Ubuntu), he was able to change the choices for file type into "any files". When he click "Upload Files", error message shown state that that type of file is not allowed.
What crossed on my mind is: an admin tried to upload .bmp image and failed. Then he rename it into .jpeg and successful. Though I don't get it what kind of images can be renamed without showing broken image logo (out of topic).
Those scenarios trigger an Exception, I'll break down what I've traced.
Trace of the codes:
app/design/frontend/base/default/catalog/product/view/media.phtml
<?php
$_img = '<img id="image" src="'.$this->helper('catalog/image')->init($_product, 'image').'" alt="'.$this->htmlEscape($this->getImageLabel()).'" title="'.$this->htmlEscape($this->getImageLabel()).'" />';
echo $_helper->productAttribute($_product, $_img, 'image');
?>
From that code, I know that image url is generated using: $this->helper('catalog/image')->init($_product, 'image')
I did Mage::log((string)$this->helper('catalog/image')->init($_product, 'image'));
Result:
http://local.m.com/media/catalog/product/cache/1/image/9df78eab33525d08d6e5fb8d27136e95/d/u/dummy.jpeg
.
Mage_Catalog_Helper_Image
public function __toString()
{
try {
if( $this->getImageFile() ) {
$this->_getModel()->setBaseFile( $this->getImageFile() );
} else {
$this->_getModel()->setBaseFile( $this->getProduct()->getData($this->_getModel()->getDestinationSubdir()) );
}
if( $this->_getModel()->isCached() ) {
return $this->_getModel()->getUrl();
} else {
if( $this->_scheduleRotate ) {
$this->_getModel()->rotate( $this->getAngle() );
}
if ($this->_scheduleResize) {
$this->_getModel()->resize();
}
if( $this->getWatermark() ) {
$this->_getModel()->setWatermark($this->getWatermark());
}
Mage::log('pass');
$url = $this->_getModel()->saveFile()->getUrl();
Mage::log('not pass');
}
} catch( Exception $e ) {
$url = Mage::getDesign()->getSkinUrl($this->getPlaceholder());
}
return $url;
}
The error triggered in $this->_getModel()->saveFile()->getUrl(). In some part of the code, it will eventually reach:
Varien_Image_Adapter_Gd2
private function _getCallback($callbackType, $fileType = null, $unsupportedText = 'Unsupported image format.')
{
if (null === $fileType) {
$fileType = $this->_fileType;
}
if (empty(self::$_callbacks[$fileType])) {
//reach this line -> exception thrown
throw new Exception($unsupportedText);
}
if (empty(self::$_callbacks[$fileType][$callbackType])) {
throw new Exception('Callback not found.');
}
return self::$_callbacks[$fileType][$callbackType];
}
The exception was catched in the previous code:
Mage_Catalog_Helper_Image
public function __toString()
{
...
} catch( Exception $e ) {
$url = Mage::getDesign()->getSkinUrl($this->getPlaceholder());
}
...
}
the $url became:
http://local.m.com/skin/frontend/default/default/images/catalog/product/placeholder/image.jpg
So, it should have generated placeholder image right?
(without ImageCdn extension)
No, because
Mage_Catalog_Helper_Image was rewritten by
OnePica_ImageCdn_Helper_Image
public function __toString()
{
parent::__toString(); //the result is http://local.m.com/skin/frontend/default/default/images/catalog/product/placeholder/image.jpg but no variable store/process its value
return $this->_getModel()->getUrl(); //in the end it will return http://local.m.com/media/catalog/product/cache/1/image/9df78eab33525d08d6e5fb8d27136e95/d/u/dummy.jpeg
}
In case you all already forgot the question:
Anybody know what is the purpose of that code? I don't understand what is the meaning of their comment above.
Is it really a bug or it is just my wrong interpretation?
No it isn't a bug. It's just legacy support for older Magento systems. I'm wondering, have you ever got around to snoop around earlier versions of magento (as the inline documentation comment references to, < 1.1.3)?
The gist of the matter is before Mage 1.1.3, Mage_Catalog_Helper_Image instances happen to produce URL's from to-string casts e.g.
$image = (some instance of Mage_Catalog_Helper_Image).. ;
$imageUrl = (string) $image;
__toString is probably either protected or private, i'm not sure but what I'm sure is the usual practice is to always code up this Magic Method in order to use it in a class that you are meaning to rewrite something with that expects to use this kind data cast.

Error with undefined method CI_Loader::plugin()

I received the following error:
Call to undefined method CI_Loader::plugin() in C:\wamp\www\Code\application\libraries\DX_Auth.php on line 1233
on this code:
function captcha()
{
$this->ci->load->helper('url');
$this->ci->load->plugin('dx_captcha');
$captcha_dir = trim($this->ci->config->item('DX_captcha_path'), './');
Make sure you have moved any array values in application/config/autoload.php from $autoload[‘plugins’] to $autoload[‘helpers’] or you will notice stuff break.
This is the reference
Which version of CI are you using? Plugins has been removed since the 2.x and replaced with helper.
Try to use reCaptcha instead, it has a good library.
You're trying to load a plugin and plugins are not supported, if I remember it right since CI version 2. If that's the case (which seems to be) you need to convert your plugins into helpers.
I think you are trying to use an old version of DX_Auth on new version of CodeIgniter. Current version of DX_Auth is compatible with CI 2.x and is available on github.
A simple way to solve this problem is that you just put this code in your loader.php. The plugin its works. Go to System->Core->Loader.php.
/**
* Load Plugin
*
* This function loads the specified plugin.
*
* #access public
* #param array
* #return void
*/
function plugin($plugins = array())
{
if ( ! is_array($plugins))
{
$plugins = array($plugins);
}
foreach ($plugins as $plugin)
{
$plugin = strtolower(str_replace(EXT, '', str_replace('_pi', '', $plugin)).'_pi');
if (isset($this->_ci_plugins[$plugin]))
{
continue;
}
if (file_exists(APPPATH.'plugins/'.$plugin.EXT))
{
include_once(APPPATH.'plugins/'.$plugin.EXT);
}
else
{
if (file_exists(BASEPATH.'plugins/'.$plugin.EXT))
{
include_once(BASEPATH.'plugins/'.$plugin.EXT);
}
else
{
show_error('Unable to load the requested file: plugins/'.$plugin.EXT);
}
}
$this->_ci_plugins[$plugin] = TRUE;
log_message('debug', 'Plugin loaded: '.$plugin);
}
}
// --------------------------------------------------------------------
/**
* Load Plugins
*
* This is simply an alias to the above function in case the
* user has written the plural form of this function.
*
* #access public
* #param array
* #return void
*/
function plugins($plugins = array())
{
$this->plugin($plugins);
}

Resources