prestashop v1.5.4.0
I want to add this click to open element made from CSS, HTML and JS located here http://codepen.io/anon/pen/Gqkxv
function openDoor(field) {
var y = $(field).find(".thumb");
var x = y.attr("class");
if (y.hasClass("thumbOpened")) {
y.removeClass("thumbOpened");
}
else {
$(".thumb").removeClass("thumbOpened");
y.addClass("thumbOpened");
}
}
what is the best method to place this in to a CMS page
My guess is since the CMS pages strip out most javascript tags and don't seem to allow you to attach exernal js files you will need to create an override of the cmsController.php.
You would need to create your external js file and css file and save them in the theme's js directory and css directory. The setMedia method is used to attach style/js files when that controller is called. You can override the cmsController.php and add this to the setMedia method
$this->addJS(_THEME_JS_DIR_.'yourjsfile.js');
$this->addCSS(_THEME_CSS_DIR_.'yourcssfile.css');
I believe that should work however, this will add these files to every cms page. The only way I can think to get around that is by getting the ID of the cms page(s) that you want it to appear on and run an if state on your addJS and addCSS functions.
example: You want it to show up on id_cms 4
if ((int)Tools::getValue('id_cms') == 4) {
$this->addJS(_THEME_JS_DIR_.'yourjsfile.js');
$this->addCSS(_THEME_CSS_DIR_.'yourcssfile.css');
}
or you want it to show on id_cms 4 and id_cms 6
if ((int)Tools::getValue('id_cms') == 4 || (int)Tools::getValue('id_cms') == 6) {
$this->addJS(_THEME_JS_DIR_.'yourjsfile.js');
$this->addCSS(_THEME_CSS_DIR_.'yourcssfile.css');
}
no need to add modules,
go to cms.tpl from your theme folder in prestashop,
add this
{if $cms->id==6}
{literal}
<script type="text/javascript" src="js/yourjsfile.js"></script>
{/literal}
{/if}
replace with your cms id and the name of your js file, then upload the file to js folder in prestahop root folder,
then go the prestahop panel, advanced parameters, performance, compile the templates and then launch your site --- the script will run only on the page selected
You can create a module and hook your js to backoffice header like this.
public function install()
{
if ( !$this->installTab()
|| !$this->registerHook('displayBackOfficeHeader'))
return false;
return true;
}
public function hookDisplayBackOfficeHeader()
{
//check if currently updatingcheck if module is currently processing update
if ($this->isUpdating() || !Module::isEnabled($this->name))
return false;
if (method_exists($this->context->controller, 'addJquery'))
{
$this->context->controller->addJquery();
$this->context->controller->addCss($this->_path.'views/css/gamification.css');
if (version_compare(_PS_VERSION_, '1.6.0', '>=') === TRUE)
$this->context->controller->addJs($this->_path.'views/js/gamification_bt.js');
else
$this->context->controller->addJs($this->_path.'views/js/gamification.js');
$this->context->controller->addJqueryPlugin('fancybox');
return $css_str.'<script>
var ids_ps_advice = new Array('.rtrim($js_str, ',').');
var admin_gamification_ajax_url = \''.$this->context->link->getAdminLink('AdminGamification').'\';
var current_id_tab = '.(int)$this->context->controller->id.';
</script>';
}
}
This a example show from prestashop core module gamification. After that you can write your own prestashop js code which you want.
In 2019 regarding PS 1.7 - we solved it here: https://www.prestashop.com/forums/topic/267834-how-to-insert-javascript-code-inside-a-page/
In short - add it directly to CMS content field with slight modifiations:
1) in class/Validation.php add
public static function isCleanHtmlWithScript($html, $allow_iframe = false)
{
$events = 'onmousedown|onmousemove|onmmouseup|onmouseover|onmouseout|onload|onunload|onfocus|onblur|onchange';
$events .= '|onsubmit|ondblclick|onclick|onkeydown|onkeyup|onkeypress|onmouseenter|onmouseleave|onerror|onselect|onreset|onabort|ondragdrop|onresize|onactivate|onafterprint|onmoveend';
$events .= '|onafterupdate|onbeforeactivate|onbeforecopy|onbeforecut|onbeforedeactivate|onbeforeeditfocus|onbeforepaste|onbeforeprint|onbeforeunload|onbeforeupdate|onmove';
$events .= '|onbounce|oncellchange|oncontextmenu|oncontrolselect|oncopy|oncut|ondataavailable|ondatasetchanged|ondatasetcomplete|ondeactivate|ondrag|ondragend|ondragenter|onmousewheel';
$events .= '|ondragleave|ondragover|ondragstart|ondrop|onerrorupdate|onfilterchange|onfinish|onfocusin|onfocusout|onhashchange|onhelp|oninput|onlosecapture|onmessage|onmouseup|onmovestart';
$events .= '|onoffline|ononline|onpaste|onpropertychange|onreadystatechange|onresizeend|onresizestart|onrowenter|onrowexit|onrowsdelete|onrowsinserted|onscroll|onsearch|onselectionchange';
$events .= '|onselectstart|onstart|onstop';
if (!$allow_iframe && preg_match('/<[\s]*(i?frame|form|input|embed|object)/ims', $html)) {
return false;
}
return true;
}
2) then in /classes/CMS.php around line #66 change
'content' => array('type' => self::TYPE_HTML, 'lang' => true, 'validate' => 'isCleanHtml', 'size' => 3999999999999)
to
'content' => array('type' => self::TYPE_HTML, 'lang' => true, 'validate' => 'isCleanHtmlWithScripts', 'size' => 3999999999999)
now you should be good to go
Related
I am trying to save an image type file by using jquery to trigger the submit. I encountered an issue where it triggers even when the change event is not done.
View create.php:
<?php \yii\bootstrap4\ActiveForm::begin([
'options' => [
'enctype' => 'multipart/form-data',
'id' => 'dynamic-form'
]
]) ?>
<button class="btn btn-primary btn-file">
Select Thumbnail
<input type="file" id="recipeThumbnail" name="recipe">
</button>
<?php \yii\bootstrap4\ActiveForm::end() ?>
In this view, i specify the id to call it in the javascipt file.
Js app.js:
$(function () {
'use strict';
$('#recipeThumbnail').change(ev => {
$(ev.target).closest('form').trigger('submit');
})
});
From my understanding here, the trigger('submit') should only run when there is a change which in this case an image file is selected. However, i suspect the event is triggered and it caused an error of Attempt to read property "name" on null from the model below on the line $this->name = $this->recipe->name;. My other suspicion would be error in the model but i cant wrap my head around where or how it could be.
Model recipe.php:
public function save($runValidaiton = true, $attributeNames = null)
{
$isInsert = $this->isNewRecord;
if ($isInsert) {
$this->recipe_id = Yii::$app->security->generateRandomString(16);
$this->name = $this->recipe->name;
}
$saved = parent::save($runValidaiton, $attributeNames);
if (!$saved) {
return false;
}
if ($isInsert) {
$recipePath = Yii::getAlias('#frontend/web/storage/thumbnail/' . $this->recipe_id . '.jpg');
if (!is_dir(dirname($recipePath))) {
FileHelper::createDirectory(dirname($recipePath));
}
$this->recipe->saveAs($recipePath);
}
return true;
}
Controller RecipeController.php:
public function actionCreate()
{
$model = new Recipe();
$model->recipe = UploadedFile::getInstanceByName('recipe');
if ($this->request->isPost) {
if (Yii::$app->request->isPost && $model->save()) {
return $this->redirect(['view', 'recipe_id' => $model->recipe_id]);
}
} else {
$model->loadDefaultValues();
}
return $this->render('create', [
'model' => $model,
]);
}
I hope i explained this well. Is this error actually caused by the js or something else and any insight on this issue is much appreciated:D
Insights from #ChrisG and #CBroe,
The issue was wrapping the input tag in a button tag which will click both button and input at the same time causing the error. An easy fix would be adding type="button" in the button tag.
But this is actually a broken HTML as an interactive element should not be nested into each other. The fix to this would be using form and labels with the button so that it is a decent html.
I am creating a word-search activity type plugin in moodle for my client and trying to use this code into moodle activity type plugin.
Source:https://ourcodeworld.com/articles/read/274/how-to-create-a-wordfind-game-soup-of-letters-in-the-browser-easily-using-javascript
but the problem is it shows here
"ReferenceError: wordfindgame is not defined"
?>
require_once(dirname(__FILE__).'/../../config.php');
global $CFG,$PAGE;
$id = optional_param( 'id', null, PARAM_INT );
if ($id) {
if (!$cm = get_coursemodule_from_id('cryptext', $id)) {
print_error('invalidcoursemodule');
}
if (!$course = $DB->get_record('course', array('id' => $cm->course))) {
print_error('coursemisconf');
}
}
require_login($course, false, $cm);
$PAGE->set_url($CFG->wwwroot.'/mod/cryptext/view.php',array('id' => $cm->id));
$context = context_module::instance($cm->id);
$PAGE->set_title('Cryptext Activity');
$PAGE->set_heading($course->fullname);
$PAGE->set_context($context); //context_system::instance()
//$PAGE->set_context(context_module::instance($id));
$PAGE->set_pagelayout('incourse');
$PAGE->requires->css($CFG->dirroot.'/mod/cryptext/style/wordfind.css');
echo $OUTPUT->header();
<div id="puzzle-container"></div>
<div id="puzzle-words"></div>
<input type="button" id="solveBTN" value="Solve puzzle"/>
<script>
// An array with the words to show
var words = ['cows', 'tracks', 'arrived', 'located', 'sir', 'seat',
'division', 'effect', 'underline', 'view', 'annual',
'anniversary', 'centennial', 'millennium', 'perennial',
'artisan', 'apprentice', 'meteorologist', 'blizzard', 'tornado',
'intensify','speed','count','consonant','someone',
'sail','rolled','bear','wonder','smiled','angle', 'absent',
'decadent', 'excellent', 'frequent', 'impatient', 'cell',
'cytoplasm', 'organelle', 'diffusion', 'osmosis',
'respiration'
];
// Start a basic word game without customization !
var gamePuzzle = wordfindgame.create(words, '#puzzle-container', '#puzzle-words');
$("#solveBTN").click(function(){
// Solve the puzzle !
var result = wordfindgame.solve(gamePuzzle, words);
console.log(result);
});
</script>
<?php
$PAGE->requires->js( new moodle_url($CFG->wwwroot . '/mod/cryptext/javascript/wordfind.js') );
$PAGE->requires->js( new moodle_url($CFG->wwwroot . '/mod/cryptext/javascript/wordfindgame.js') );
echo $OUTPUT->footer();
ReferenceError: wordfindgame is not defined[Learn More]
view.php:716:17
I have included the wordfid.js and wordfindgame.js and i went to page source code and clicked the js file link which showed the file content in the browser. It will be very helpful if someone could help me out in solving this issue thanks in prior.
Method 1:
$PAGE->requires->js method accepts second parameter to include file in header. By default second parameter is 'false'. You can send 'true' to include it in header to use it without any conflicts.
Method 2:
You can write your JS code after dependencies ( wordfindgame.js and wordfind.js files in your case) loaded. You can check where it loaded in web page from page source of the browser.
On my magento product page I need to add a dynamic JavaScript array base on display upselling products on the product page. The goal is to change the images of the upselling products when the user change the color of the main product.
To achieve my goal I need a custom JavaScript array on every product page that give me information about crossselling product and the associated product image.
What is the best way to do this?
I try this
add a observer event in my config.xml
<controller_action_layout_load_before>
<observers>
<crossselling_product_view>
<type>singleton</type>
<class>XXXXXXXX_Crossselling_Model_Observer</class>
<method>productview</method>
</crossselling_product_view>
</observers>
</controller_action_layout_load_before>
add observer to add specific JS Code
<?php
class XXXXXXXX_Crossselling_Model_Observer {
public function productview(Varien_Event_Observer $observer) {
$product = Mage::registry('current_product');
//only on product page
if (!($product instanceof Mage_Catalog_Model_Product)) {
return;
}
$controller = $observer->getAction();
$layout = $controller->getLayout();
$block = $layout->createBlock('core/text');
$block->setText(
'<script type="text/javascript">
function main_pulsestorm_hellojavascript()
{
alert("Foo");
}
main_pulsestorm_hellojavascript();
</script>'
);
$layout->getBlock('head')->append($block);
}
}
My error:
Fatal error: Call to a member function append() on a non-object
What is my problem and is it the right way to add dynamic js code?
I would probably approach this from a different angle. Since you are only interested in interacting with data and output for the upsell block, you could change the behavior of just that block by observing its output and appending your extra JavaScript. For the purposes of brevity this answer assumes that you understand the basics of Magento extensions.
Observe the core_block_abstract_to_html_after event:
etc/config.xml
<core_block_abstract_to_html_after>
<observers>
<addCustomUpsellFormat>
<class>XXXXXXXX_Crossselling_Model_Observer</class>
<method>addCustomUpsellFormat</method>
</addCustomUpsellFormat>
</observers>
</core_block_abstract_to_html_after>
Act upon instances of Mage_Catalog_Block_Product_List_Upsell by appending the output of a new block that will read their data:
Model/Observer.php
public function addCustomUpsellFormat(Varien_Event_Observer $observer)
{
/* #var Mage_Core_Block_Abstract $block */
$block = $observer->getBlock();
if ($block instanceof Mage_Catalog_Block_Product_List_Upsell) {
/* #var Varien_Object $transport */
$transport = $observer->getTransport();
// Receive the standard output for the block.
$output = $transport->getHtml();
/* #var Mage_Core_Model_Layout $layout */
$layout = $block->getLayout();
$json = $layout->createBlock('core/template')
->setTemplate('catalog/product/list/upsell_json.phtml')
->setItems($block->getItems())
->toHtml();
// Append new JSON data to block output.
$transport->setHtml($output . $json);
}
return $this;
}
Create a template that interprets the upsell data and outputs it in your desired way, in my example above I created a template that could do something like this (my example creates a new template, so it should go into the base theme):
app/design/frontend/base/default/template/catalog/product/list/upsell_json.phtml
<?php
$_json = array(); // Add data in here to convert to JSON for output.
$_items = $this->getItems();
/* #var Mage_Catalog_Model_Product $_product */
foreach ($_items as $_product) {
$_json[$_product->getId()] = array(
'image' => (string)Mage::helper('catalog/image')->init($_product, 'image')
);
}
?>
<script type="text/javascript">var upsellData = <?php echo json_encode($_json) ?>;</script>
Use
$controller = $observer->getEvent()->getAction();
instead of
$controller = $observer->getAction();
I have 3 seperate dataProviders for my Gridview, one with Saved data, one with Unsaved data and one with both.
Now this is what I'm trying to accomplish:
If you click on saved, the dataProvider changes to the one with saved data.
I'm trying it like this:
<?php
if($i == 1){
$dataProvider = $dataProviderSaved;
} elseif($i == 2) {
$dataProvider = $dataProviderNotsaved;
} else {
$dataProvider = $dataProviderBoth;
};
\yii\widgets\Pjax::begin(['id' => 'gridview', 'timeout' => false,
'enablePushState' => false, 'clientOptions' => ['method' => 'POST']]) ?>
<?= GridView::widget([
'dataProvider' => $dataProvider,
//regular gridview..
\yii\widgets\Pjax::end(); ?>
Javascript:
var i = $i;
$("#saved").click(function(){
i=1;
$.pjax.defaults.timeout = false;//IMPORTANT
$.pjax.reload({container:"#gridview"});
});
', \yii\web\View::POS_READY);
So, I've just read that changing PHP variables inside JS is 'impossible'.
How would I accomplish this?
Is there a better way?
Do I need 3
DataProviders? (This means 3 find()'s inside of the controller)
If I understood properly you don't need 3 dataProviders. You should use GridView's FilterSelector option to treat that external element as part of GridView's filter.
For example
echo GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'filterSelector' => "input[name='ModelSearch[_selection]'],",
...
Then, in your Filter Model you filter depending on that value
switch($this->_selection) {
case 'Saved':
$query->andFilterWhere([...]);
break;
case 'Unsaved':
$query->andFilterWhere([...]);
break;
case 'Both':
$query->andFilterWhere([...]);
break;
Don't forget to add the _selection attribute to your Model class and to rules() as 'safe' in the Search Model class.
You can try in two ways:
The first the simpler you assign to each button (saved, unsaved, both) the call of three separate cation of your controller
that invoke each a respective view connected to a single gridview each of these latter with the appropriate dataprovider
The second consists of controller you have the three dataprovider different as this example
return $this->render('viewYourView', [
'/modelContribuente' =>$modelContribuente,
'dataProviderOne' => $providerOne,
'dataProviderTwo' => $providerTwo,
'dataProviderThree' => $providerThree,
]);(
In a single View you can create your three gridview everyone who uses the dataprovider appropriate and then visalizzare or hide gridviewn with JQuery functions controlled by buttons
I am writing a code to use onchange in my application this is my code so far
.Phtml
<script type="text/javascript">
function submit()
{
$id = intval($_GET['id']);
$satellite = intval($_GET['satellite_id']);
if ($id == 0)
{
echo "Please select a Region";
}
else
{
$query = "select * from satellites where region_id = '".$id."'";
$query = mysql_query($query);
echo "<select name='satellite_id'><option value=''>-- select one --</option>";
while ($row = mysql_fetch_assoc($query))
{
echo "<option value='".$row['satellite_id']."'".($row['satellite_id']==$satellite?" selected":"").">".$row['satellite_name']."</option>";
}
echo "</select>";
//DisplayFormRow ("Satellite", FormDropDownBox ("satellite_id", $SatelliteARY, $Result['satellite_id']));
}
}
</script
//zend code Form
$region_name = new Zend_Form_Element_Select('region_name');
$region_name->setAttribs(array('style' => 'width: 150px;'));
$region_name ->setLabel('Region')
->onchange('this.form.submit();') //tried this code ->onchange('javascript:submit();')
->addMultiOption('--Select One--', '--Select One--');
$mdlRegions = new Model_Regions();
$regions = $mdlRegions->getRegions();
foreach ($regions as $region)
{
$region_name->addMultiOption($region->region_id, $region->region_name, $region->region_short_name);
}
//model
<?php
class Model_Regions extends Zend_Db_Table_Abstract
{
protected $_name = 'regions';
//protected $_name = 'smmedetails';
public function getregion($region_id)
{
$region_id = (int)$region_id;
$row = $this->fetchRow('region_id = ' . $region_id);
if (!$row) {
throw new Exception("Could not find row $region_id");
}
return $row->toArray();
}
public function smmedetails2region($region_name)
{
$data = array(
'region_name'=> $region_name
);
return $this->insert($data);
}
public function getRegions()
{
$select = $this->select();
return $this->fetchAll($select);
}
}
//controller
public function registerAction()
{
$this->view->headScript()->appendFile('/js/ui/jquery.ui.autocomplete.js');
$form = new Form_SmmeDetails();
$this->view->form = $form;
if ($this->getRequest()->isPost()) {
$formData = $this->getRequest()->getPost();
if ($form->isValid($formData)) {
$companyname = $form->getValue('companyname');
$companytradingname = $form->getValue('companytradingname');
$region_name = $form->getValue('region_name');
$satellite_name = $form->getValue('satellite_name');
$city = $form->getValue('city');
$companyaddress = $form->getValue('companyaddress');
$addresscode = $form->getValue('addresscode');
$companypostaladdress = $form->getValue('companypostaladdress');
$postalcode = $form->getValue('postalcode');
$companyphonenumber = $form->getValue('companyphonenumber');
$companyfaxnumber = $form->getValue('companyfaxnumber');
$companycellnumber = $form->getValue('companycellnumber');
$businessemailaddress = $form->getValue('businessemailaddress');
$businesswebsite = $form->getValue('businesswebsite');
$smmedetails = new Application_Model_DbTable_SmmeDetails();
$smmeid = $smmedetails ->smmedetailsSmmeDetails($companyname, $companytradingname, $region_name, $satellite_name, $city, $companyaddress, $addresscode, $companypostaladdress, $postalcode, $companyphonenumber, $companyfaxnumber,
$companycellnumber, $businessemailaddress, $businesswebsite);
// $region = new Application_Model_DbTable_Region();
//$region ->smmedetails2region($formData, $smmedetails->smmeid);
$this->_redirect('/admin/smme/register2/smmeid/'.$smmeid);
} else {
$form->populate($formData);
}
}
}
The code is suppose to view a hidden input select, called satellite when you select a feild option from regions, the satellite should view certain options based on the region selected. In short the region selected should correspond with what the user selected. eg Province is Gauteng, so cites would be, Johannseburg,Pretoria etc. Take note the region and satellite options are called from the database table according to they names and id. The code above keeps giving me and error Message: Method onchange does not exist. Was told not to use onchange method should I be using ajax and can I use javascript and sqlquery in the view or should I call it as an action? If so how? Here is a slight picture example.
Please be of help
Thanks in advance
I'd make a few suggestions to what you have there.
Firstly, for simplicity, I'd not use the onChange function, because I don't see it in the API, plus JavaScript or jQuery written in that way can become difficult to maintain and write properly. It is a lot simpler to instead include an external JavaScript file. By doing this, you can also test and debug the JavaScript separately, as well as reuse it.
Have a look at the excellent document for onChange, and getJson. I've used these and others and they're quite straight-forward. For testing, I recommend QUnit for starters. It makes testing a breeze.
Secondly, if you're using the Zend libraries for Model_Regions and $region_name, then I'd suggest using them instead of the direct mysql calls as well. This will allow you to build a good library which you can continue to expand as needed, plus it makes composing SQL quicker and safer.
For the controller, I'd suggest a RestController with a Restful Route. Here's an excellent tutorial.
I hope this helps you out with the problem. If you need anything more, let me know.
Thanks for emailing me about this.
The way I go about this is as follows:
Firstly I set up the form, and then an action in a controller.
Lets say getmajorgroupAction()
which in that action I would then disable layout, and just get the relevent results based on the id.
Then in the view file, loop through the
so the output from that call would be
<option value="1">1</option>
<option value="2">2</option>
etc
Personally I use jquery now, whereas the post you referenced when you emailed me, I was using another method.
trying to use something like this
jQuery(document).ready(function() {
jQuery("#division").change(function () {var division = jQuery("#division").val();
jQuery("#major_group").load("/module/getmajorgroup/", {division_id: division} );});
});
Hope that makes sense.
Thanks that was useful but i found a way to do it using this formula below, but everytime I click on the first select the while still in the session the second select appears all the time, eg if a person choose the wrong selection and re tried it brings up another field instead of fixing the field. I think its in a countinous loop . heres my script
<script type="text/javascript">
$(document).ready(function() {
$("#region_name").on('change', function () {
ajaxAddField();
}
);
}
);
// Retrieve new element's html from controller
function ajaxAddField()
{
$.ajax(
{
type: "POST",
url: '<?php echo $this->baseURL()?>/admin/ajax/get-cities/city/' + encodeURIComponent($('#region_name').val()),
success: function(newElement) {
// Insert new element before the Add button
//$(this).prev().remove().end().before('#city-label');
$("#city-label").before(newElement);
}
}
);
}
</script>