OpenCart 3 common/header variable in extension - opencart

I would like to add a variable/display in common/header twig file, which can be managed from an new extension. The new extension is created. starter_module
I added in:
admin/view/template/extension/module/starter_module.twig
<div class="form-group">
<label class="col-sm-2 control-label" for="input-new">New</label>
<div class="col-sm-10">
<select name="new" id="input-new" class="form-control">
{% if new %}
<option value="1" selected="selected">Enabled</option>
<option value="0">Disabled</option>
{% else %}
<option value="1">Enabled</option>
<option value="0" selected="selected">Disabled</option>
{% endif %}
</select>
</div>
</div>
in admin/controller/extension/module/starter_module.php
if (isset($this->request->post['new'])) {
$data['new'] = $this->request->post['new'];
} elseif (!empty($module_info)) {
$data['new'] = $module_info['new'];
} else {
$data['new'] = '';
}
in catalog/controller/extension/module/starter_module.php
$data['new'] = $this->config->get('new');
$data['new'] = (int) $setting['new'];
in catalog/view/theme/default/template/common/header.twig
{% if new %}Enabled {% else %} disabled{% endif %}
But always I got the result only disabled, what is missing? cannot be sent variable from extension to common header?
Please, help me if you know the issue, the non working files are here https://github.com/bblori/Opencart3
You can see here one of my working variable which was set in setting/setting files and is working.
https://github.com/bblori/Enable-Style-OC3
XML code
<modification>
<name>Starter Module</name>
<code>starter-module</code>
<version>1.0.0</version>
<author>Author</author>
<link>http://domain/</link>
<file path="catalog/controller/common/header.php">
<operation>
<search><![CDATA[return $this->load->view('common/header', $data);]]></search>
<add position="before"><![CDATA[
$data['config_new'] = $this->config->get('config_new');
]]></add>
</operation>
</file>
Many thanks

move your code from your starter module to header.php
$data['new'] = $this->config->get('new');
$data['new'] = (int) $setting['new'];

Neither editing core files or using vqmod is acceptable. Core files should not be modified, because later updates would make your modifications obsolete. Vqmod on the other hand adds an unnecessary complexity to a system well designed.
Since version 3 Opencart team have introduced events. Events are a new way of executing custom functionality when it's needed. Next time your have a similar problem add event (either manually or during installation of your module as shown below).
public function install() {
$this->load->model('setting/event');
$this->model_setting_event->addEvent('my_data_manager', 'catalog/view/*/before', 'extension/module/my_data_manager/beforeAll');
}
Later during execution cycle your function will be called automatically every time common/header is being rendered.
class ControllerExtensionModuleMyDataManager extends Controller {
public function beforeAll(&$route, &$data, &$output){
if ($route == 'common/header') {
$data['my_custom_data'] = 'Mickey Mouse is not a bird!';
}
}
}
Finally, add {{ my_custom_data }} to template/common/header.twig

Related

Bootstrap-Select not updating on mobile

I am using Bootstrap-select for multi-select items with Django. It works fine on desktop, but when the native mobile drop-down is enabled, the selected values of the dropdown do not populate.
HTML
<!-- start product brand info -->
<div class="row">
<div class="col-md-6 col-xs-*">
<div>
<label for="id_brand-name" class="form-label">Products</label>
</div>
<select multiple class="form-control selectpicker mb-3" id="id_brand-name" name="brand-name" mobile="true" multiple required>
{% for product in products %}
<option value="{{product.id}}" id="optiion">{{ product.brand.name | title }} - {{product.name | title}}</option>
{%endfor%}
</select>
<div class="invalid-feedback">
Select at least one product.
</div>
</div>
</div>
<!-- end product info -->
<script>
//Enble native drop down on mobile
window.onload = function () {
$('#id_brand-name').selectpicker({
container: 'body'
});
if( /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent) ) {
$('#id_brand-name').selectpicker('mobile')
}};
</script>
No matter how many items are selected, the selector always shows Nothing selected. When the data is posted, it is apart of the form though.
I like to use bootstrap-select, but this problem has been bothering me for a long time
Similar questions can be found on github, but no perfect answer.
The reason is that it will always refresh his title, no matter how remove() is done externally, and you can't change it's style on iphone Safari and Firefox.
So my solution is: if you can't remove it, then join it.
You have to change the original file: bootstrap-select.js
Search for: bs-title-option, next line you will find:
this.selectpicker.view.titleOption.value = '';
And add two lines down:
this.selectpicker.view.titleOption.value = '';
// new add
this.selectpicker.view.titleOption.disabled='true';
this.selectpicker.view.titleOption.textContent=this.options.title;
//
Done
When you use $('#id_brand-name').selectpicker('mobile')
The title text show on first empty option, and it can't be choose.
try it!

Sitecore WFFM-Webforms customize html structure

I am Using WFFM with Sitecore-8.2-Update 4 Version. So i have a requirement to change HTML structure of WFFM webforms.
Let me share my case.
We have Name Section that contains first name and last name.
IN WFFM form designer i have created like below
Then the Html is generated like below by WFFM.
<div class="required-field form-group">
<input id="wffm26cf3ccb39024ed0974e0f60d9292156_Sections_0__Fields_0__Id" name="wffm26cf3ccb39024ed0974e0f60d9292156.Sections[0].Fields[0].Id" type="hidden" value="{6DAF46AC-CEE6-4CB2-9391-40ACC39CCE74}" />
<label class="control-label" for="wffm26cf3ccb39024ed0974e0f60d9292156_Sections_0__Fields_0__Value">LastName</label>
<input class=" form-control text-box single-line" data-val="true" data-val-length="The LastName field must be a string with a minimum length of 0 and a maximum length of 256." data-val-length-max="256" data-val-required="This field is required." data-val-required-tracking="{7E86B2F5-ACEC-4C60-8922-4EB5AE5D9874}" id="wffm26cf3ccb39024ed0974e0f60d9292156_Sections_0__Fields_0__Value" name="wffm26cf3ccb39024ed0974e0f60d9292156.Sections[0].Fields[0].Value" placeholder="" style="" type="text" value="" /><span class="field-validation-valid help-block" data-valmsg-for="wffm26cf3ccb39024ed0974e0f60d9292156.Sections[0].Fields[0].Value" data-valmsg-replace="true">
</span>
</div>
<div class="required-field form-group">
<input id="wffm26cf3ccb39024ed0974e0f60d9292156_Sections_0__Fields_1__Id" name="wffm26cf3ccb39024ed0974e0f60d9292156.Sections[0].Fields[1].Id" type="hidden" value="{FF409749-4574-4D8C-971D-1A2D66B82FDA}" />
<label class="control-label" for="wffm26cf3ccb39024ed0974e0f60d9292156_Sections_0__Fields_1__Value">FirstName</label>
<input class=" form-control text-box single-line" data-val="true" data-val-length="The FirstName field must be a string with a minimum length of 0 and a maximum length of 256." data-val-length-max="256" data-val-required="This field is required." data-val-required-tracking="{7E86B2F5-ACEC-4C60-8922-4EB5AE5D9874}" id="wffm26cf3ccb39024ed0974e0f60d9292156_Sections_0__Fields_1__Value" name="wffm26cf3ccb39024ed0974e0f60d9292156.Sections[0].Fields[1].Value" placeholder="" style="" type="text" value="" /><span class="field-validation-valid help-block" data-valmsg-for="wffm26cf3ccb39024ed0974e0f60d9292156.Sections[0].Fields[1].Value" data-valmsg-replace="true">
</span>
</div>
But I need a Html like below one
<div class="required-field form-group has-feedback">
<span>
<input class=" form-control text-box single-line" data-val="true" data-val-length="The First field must be a string with a minimum length of 0 and a maximum length of 256." data-val-length-max="256" data-val-required="This field is required." data-val-required-tracking="{7E86B2F5-ACEC-4C60-8922-4EB5AE5D9874}" id="wffm26cf3ccb39024ed0974e0f60d9292156_Sections_0__Fields_1__Value" name="wffm26cf3ccb39024ed0974e0f60d9292156.Sections[0].Fields[1].Value" placeholder="" style="" type="text" value="">
<input id="wffm26cf3ccb39024ed0974e0f60d9292156_Sections_0__Fields_1__Id" name="wffm26cf3ccb39024ed0974e0f60d9292156.Sections[0].Fields[1].Id" type="hidden" value="{FF409749-4574-4D8C-971D-1A2D66B82FDA}">
<label class="control-label" for="wffm26cf3ccb39024ed0974e0f60d9292156_Sections_0__Fields_1__Value">First</label>
<span class="field-validation-valid help-block" data-valmsg-for="wffm26cf3ccb39024ed0974e0f60d9292156.Sections[0].Fields[1].Value" data-valmsg-replace="true">
</span>
</span>
<span>
<input class=" form-control text-box single-line" data-val="true" data-val-length="The Last field must be a string with a minimum length of 0 and a maximum length of 256." data-val-length-max="256" data-val-required="This field is required." data-val-required-tracking="{7E86B2F5-ACEC-4C60-8922-4EB5AE5D9874}" id="wffm26cf3ccb39024ed0974e0f60d9292156_Sections_0__Fields_0__Value" name="wffm26cf3ccb39024ed0974e0f60d9292156.Sections[0].Fields[0].Value" placeholder="" style="" type="text" value="">
<input id="wffm26cf3ccb39024ed0974e0f60d9292156_Sections_0__Fields_0__Id" name="wffm26cf3ccb39024ed0974e0f60d9292156.Sections[0].Fields[0].Id" type="hidden" value="{6DAF46AC-CEE6-4CB2-9391-40ACC39CCE74}">
<label class="control-label" for="wffm26cf3ccb39024ed0974e0f60d9292156_Sections_0__Fields_0__Value">Last</label>
<span class="field-validation-valid help-block" data-valmsg-for="wffm26cf3ccb39024ed0974e0f60d9292156.Sections[0].Fields[0].Value" data-valmsg-replace="true">
</span>
</span>
</div>
Steps that i followed change html structure of WFFM.
WFFM using Sitecore.Forms.Mvc.Controllers.FormController and Its using default index action. By this it is using ~\Views\Forms\Index.cshtml on root web site.
Its basically using Editor for template.
So in my case i need to find out name section and need to change my html structure. From my findings SectionViewModel.cshtml is responsible for rendering all sections.
I have create one method to check whether current section needs to customized or not.
#if
(Renderer.IsCustomisedSection(Model))
Below my full SectionViewModel view file. (~\Views\Form\EditorTemplates\SectionViewModel.cshtml)
#using Sitecore.Forms.Mvc.Html
#using AAA.Foundation.HtmlHelper.WffmRenderer;
#model Sitecore.Forms.Mvc.ViewModels.SectionViewModel
<!--Render customised action-->
#if (Renderer.IsCustomisedSection(Model))
{
var customisedFieldHtml = Html.EditorFor(x => Model.Fields);
#Html.Raw(Renderer.RenderCustomisedFieldSection(customisedFieldHtml))
}
else
<!--Render customised action Ends-->
if (Model.ShowTitle && !string.IsNullOrEmpty(Model.Title))
{
<fieldset class="#Model.CssClass">
<legend>#Html.BootstrapText("Title")</legend>
#if (Model.ShowInformation && !string.IsNullOrEmpty(Model.Information))
{
<p>#Html.BootstrapText("Information")</p>
}
<div class="row">
<div class="col-md-12">
#Html.EditorFor(x => Model.Fields)
</div>
</div>
</fieldset>
}
else
{
#Html.EditorFor(x => Model.Fields)
}
This is how i can catch Name section by model title.Below my renderer class file
public static bool IsCustomisedSection(Sitecore.Forms.Mvc.ViewModels.SectionViewModel model) {
bool result = false;
// Check the model needs to customised
if (model.Title == "Name") { // For Ex:
result = true;
}
return result;
}
Then i will call respective customize html section like below.
var customisedFieldHtml = Html.EditorFor(x => Model.Fields);
#Html.Raw(Renderer.RenderCustomisedFieldSection(customisedFieldHtml))
Using "Html.EditorFor(x => Model.Fields)" this i can get default generated html. Then i will pass it to my customise render method. This will identify which sites and which section rendere needs to apply.
public static HtmlString RenderCustomisedFieldSection(HtmlString
customisedSectionHtml)
{
string caseValue = "SiteName-SectionName";
HtmlString result = new HtmlString(string.Empty);
switch (caseValue) {
case "SiteName-SectionName":
result = SiteName_Renderer.NameSection(customisedSectionHtml);
break;
}
return result;
}
My final customised html rendere method like below. I am using html agility pack to reframe the htmls.
public static HtmlString NameSection(HtmlString rawHtml)
{
HtmlNode customisedRoot = null;
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(rawHtml.ToString());
// Find each form group elements
var groupHtml = doc.DocumentNode
.Descendants("div")
.Where(d =>
d.Attributes.Contains("class")
&&
d.Attributes["class"].Value.Contains("form-group")
);
if (groupHtml.Count() > 0)
{
// Initialise root
customisedRoot = groupHtml.First().Clone();
customisedRoot.RemoveAllChildren();
// Customised the html
foreach (HtmlNode htmlNode in groupHtml)
{
HtmlDocument innerRootDoc = new HtmlDocument();
HtmlNode innerRoot = innerRootDoc.CreateElement("span");
var helpBlock = htmlNode.Descendants("span").Where(d =>
d.Attributes.Contains("class")
&&
d.Attributes["class"].Value.Contains("help-block")
);
innerRoot.PrependChild(helpBlock.FirstOrDefault());
var label = htmlNode.Descendants("label");
innerRoot.PrependChild(label.FirstOrDefault());
var inputs = htmlNode.Descendants("input");
foreach (HtmlNode inputsNode in inputs)
{
innerRoot.PrependChild(inputsNode);
}
customisedRoot.PrependChild(innerRoot);
}
}
return (customisedRoot!=null)?new HtmlString(customisedRoot.OuterHtml): new HtmlString(String.Empty);
}
By this approach i can able to customize WFFM web form html.
Below are i have tested after my html changes.
Does validation working or not in website.
If i submit the form will it available in form-reports section.In download excel whether submitted value will present or not.
So all the above cases or passed.
My query is above approach will break any other functionality.

Display Form Field based on other Fields within the Django Form

I am trying to create a way to only display certain fields within a django form based on the bound data of another field within that same form. I'm familiar with the idea of form.field.bound_type but I'm not sure how to continually check for state change on a field in a form and update the other field accordingly. Something like if you were filling out an application and it asked if you've committed a crime, if you click yes then a details text area pops up.
I'm using:
Django 1.8.4
Bootstrap3 6.6.2
As it pertains to this question. Here is what I've currently got with the field values edited for work protection. It does SORT of work. Meaning the form is fine, the if statement works initially but it doesn't reevaluate the if once the specified field has changed.
<form action= "/send_email/" method="post" class='col-sm-5'>
{% csrf_token %}
{% bootstrap_field form.field_one%}
{% bootstrap_field form.field_two%}
{% bootstrap_field form.field_three%}
{% if form.field_three.bound_data == "A Value" %}
{% bootstrap_field form.field_four%}
{% endif %}
{% buttons %}
<button type="submit" class="btn btn-primary">
{% bootstrap_icon "glyphicon glyphicon-ok-circle" %} Submit
</button>
{% endbuttons %}
</form>
Solution:
With Birdie's help I was able to figure out the solution. For anyone who has hit this same Django related problem here is how you add or remove fields based on another field in the same form.
<script>
// function that hides/shows field_four based upon field_three value
function check_field_value(new_val) {
if(new_val != 'A value') {
// #id_field_four should be actually the id of the HTML element
// that surrounds everything you want to hide. Since you did
// not post your HTML I can't finish this part for you.
$('#field_four').removeClass('hidden');
} else {
$('#field_four').addClass('hidden');
}
}
// this is executed once when the page loads
$(document).ready(function() {
// set things up so my function will be called when field_three changes
$('#field_three').change( function() {
check_field_value(this.value);
});
});
</script>
<form action= "/send_email/" method="post" class='col-sm-5'>
{% csrf_token %}
{% bootstrap_field form.field_one%}
{% bootstrap_field form.field_two%}
<div id="field_three">{% bootstrap_field form.field_three%}</div>
<div id="field_four">{% bootstrap_field form.additional_notes %}</div>
{% buttons %}
<button type="submit" class="btn btn-primary">
{% bootstrap_icon "glyphicon glyphicon-ok-circle" %} Submit
</button>
{% endbuttons %}
</form>
You cannot do this within the template because the template is executed on the server side, but the user interaction occurs on the client side.. in the browser. This must be done in javascript and run in the browser.
Here is an example of jQuery code which does this. I did not test it so it may need tweaking, but this should get you in the right direction.
You will need to look at your HTML to determine the id of the element you actually want to hide() and show(). Normally you would have some kind of HTML element (eg. a DIV) surrounding both the field or fields you want to hide as well as the label(s).. and you would hide everything at once by hiding the element which contains all the fields, rather than each individual field itself.
If you add the HTML surrounding field_four to your question, I will update the answer to work with what you've got...
<script>
// Ideally this script (javascript code) would be in the HEAD of your page
// but if you put it at the bottom of the body (bottom of your template) that should be ok too.
// Also you need jQuery loaded but since you are using bootstrap that should
// be taken care of. If not, you will have to deal with that.
// function that hides/shows field_four based upon field_three value
function check_field_value() {
if($(this).val() == 'A Value') {
// #id_field_four should be actually the id of the HTML element
// that surrounds everything you want to hide. Since you did
// not post your HTML I can't finish this part for you.
$('#id_field_four').hide();
} else {
$('#id_field_four').show();
}
}
// this is executed once when the page loads
$(document).ready(function() {
// set things up so my function will be called when field_three changes
$('#id_field_three').change(check_field_value);
// set the state based on the initial values
check_field_value.call($('#id_field_three').get(0));
});
</script>
Thanks all for the contribution, but I could not get the code above work. This is my solution:
<script>
function hideField() {
check = document.getElementsByName("put your field name here")[0].value;
response = document.getElementById("put your id here");
if (check === "Open") {
response.style.display = "none";
}else{
response.style.display = "block";
}
}
</script>
The key is to figure out Id for the field you want to hide, and name for the field you check. I did NOT use DIV container to assign the id and the name, but inspected rendered HTML page with developer tools in the browser. Let me know if you have questions or want more detailed explanation.
I found this question very interesting. I have updated it with bootstrap 4.0v, with the following script, I hope it can help someone:
<script>
// function that hides/shows field_four based upon field_three value
function check_field_value(new_val) {
if(new_val != 'A value') {
// #id_field_four should be actually the id of the HTML element
// that surrounds everything you want to hide. Since you did
// not post your HTML I can't finish this part for you.
$('#field_four').removeClass('d-none');
} else {
$('#field_four').addClass('d-none');
}
}
// this is executed once when the page loads
$(document).ready(function() {
// set things up so my function will be called when field_three changes
$('#field_three').change( function() {
check_field_value(this.value);
});
});
</script>
<form action= "/send_email/" method="post" class='col-sm-5'>
{% csrf_token %}
{% bootstrap_field form.field_one%}
{% bootstrap_field form.field_two%}
<div id="field_three">{% bootstrap_field form.field_three%}</div>
<div id="field_four">{% bootstrap_field form.additional_notes %}</div>
{% buttons %}
<button type="submit" class="btn btn-primary">
{% bootstrap_icon "glyphicon glyphicon-ok-circle" %} Submit
</button>
{% endbuttons %}
</form>

Dynamic dropdown list without ASP.NET

I want to create something like that:
3.bp.blogspot.com/-RlGu3mmu6jA/URJdWWtt9XI/AAAAAAAADoU/ryaRZ3DKkzc/s1600/1.gif
but without ASP.NET.
Is that possible somehow?
As others have stated, you can easily use client-side code such as Javascript.
Here is an example using Javascript and jQuery: http://jsfiddle.net/ET5JW/9/
HTML:
<label for="firstBox">First Select</label>
<select id="firstBox">
<option value="">Select Option...</option>
<option value="a">A</option>
<option value="b">B</option>
</select>
<br />
<div id="secondBox_frame" style="display:none;">
<label for="secondBox">Second Select</label>
<select id="secondBox">
<option value="">Use first box first</option>
</select>
</div>
Javascript (with jQuery):
var options = new Array("a","b");
options["a"] = new Array("1a","2a","3a");
options["b"] = new Array("1b","2b","3b");
$("#firstBox").change(function(){
if ($("#firstBox").val()) {
$("#secondBox").html('');
var selectedOptions = options[$("#firstBox").val()];
for (var i in selectedOptions) {
$("#secondBox").append('<option value="'+selectedOptions[i]+'">'+selectedOptions[i]+'</option>');
}
$("#secondBox_frame").fadeIn(400);
}
else {
$("#secondBox").html('<option value="">Use first box first</option>');
$("#secondBox_frame").fadeOut(400);
}
});
If you are interested in doing this server-side PHP could help.
Is that possible somehow?
yes, um ... but what language do you want to use? do you want something like that for a web page (here)? or in an desktop programm (e.g. java - swing)? android/iOS app ?

Meteor with i18next cookies

I have a simple login page with a "option" for select language :
<input type="text" class="form-control" id="identifiant" data-i18n="[placeholder]login.placeholders.username" autofocus>
<input type="password" class="form-control" id="password" data-i18n="[placeholder]login.placeholders.password">
<input id="loginCheckbox" type="checkbox" value="remember-me"/> <label for="loginCheckbox" data-i18n="login.rememberme"></label>
<br>
<center>
<div class="form-group col-lg-6 col-lg-offset-3">
<select id="select-lang" class="form-control">
<option value="en-US" data-i18n="lang.english"></option>
<option value="fr-FR" data-i18n="lang.french"></option>
</select>
</div>
The JS part :
if(Meteor.isClient) {
Meteor.startup(function() {
i18n
.init({
fallbackLng:'en-US',
})
.done(function() {
$('[data-i18n]').i18n();
});
});
}
When you change the language on the login page everything work perfectly :). But when I log a user, I lost the translation for other pages. My question is : How can I save the language setting for all my site ? Cookies ?
Sorry, I'm new with i18next :)
Yep, cookies are the way to go. Session is not enough since it's not persistent.
Cookie.set('lang', 'en-US');
Cookie.get('lang');
Use Meteor Session.
if(Meteor.isClient) {
Meteor.startup(function() {
if(typeof Session.get('lang') == 'undefined') {
//set default lang
Session.set('lang', 'en-US');
}
i18n
.init({
fallbackLng: Session.get('lang'),
})
.done(function() {
$('[data-i18n]').i18n();
});
});
And change the Session when changing language.
Session is active until user closes his browser. If you want to preserve chosen language after re-opening browser store the lang in collection.