Create menu item in joomla programmatically - joomla

I've created a Joomla 3 component that will add some menu items programmatically. I've searched how to do it and I found this old question/answer:
Programmatically Create Menu Item in Joomla
It suggest to use this code:
//add the article to a menu item
$menuTable = JTable::getInstance('Menu', 'JTable', array());
$menuData = array(
'menutype' => 'client-pages',
'title' => $data[name],
'type' => 'component',
'component_id' => 22,
'link' => 'index.php?option=com_content&view=article&id='.$resultID,
'language' => '*',
'published' => 1,
'parent_id' => '1',
'level' => 1,
);
// Bind data
if (!$menuTable->bind($menuData))
{
$this->setError($menuTable->getError());
return false;
}
// Check the data.
if (!$menuTable->check())
{
$this->setError($menuTable->getError());
return false;
}
// Store the data.
if (!$menuTable->store())
{
$this->setError($menuTable->getError());
return false;
}
It works but have the same problem as shown on the question I linked.
Showing the error: Warning: str_repeat() [function.str-repeat]: Second argument has to be greater than or equal to 0 in .....
And someone suggest this solution I can't understand.
Try using JTableNested::setLocation($referenceId, $position = 'after'):
$table->setLocation($parent_id, 'last-child');
I also think that you need to rebuild the path:
// Rebuild the tree path.
if (!$table->rebuildPath($table->id)) {
$this->setError($table->getError());
return false;
}
Could someone explain me where to use that solution?

Related

Disable row clicking action helperList Prestashop

I have created a helperList and defined the rowActions using addRowAction view, edit and delete. Everything works fine but I have noticed that on clicking the row not the buttons, the action of the first rowAction button i.e. 'view' in this case is performed. How can I disable it such that only the button click performs the action while clicking anywhere else on the table i.e. on the rows does nothing. I am working on prestashop 1.7.
I searched for the issue and found the solution for that, Hope It will help you
If you want to remove the click you have to define it in your field list for each column with 'remove_onclick' => true,
$this->fields_list = [
'id' => [
'title' => $this->l('Id'),
'type' => 'text',
'align' => 'center',
'search' => false,
'remove_onclick' => true,
],
];
or $helper->no_link = true; in Helper Form
$helper = new HelperList();
$helper->no_link = true;
Try to add "$this->list_no_link = true;" in "__construct()" function.
public function __construct()
{
...
...
$this->list_no_link = true;
}

Creating configurable products programmatically - pricing_value not saved

I have a custom xml file and I make a massive import of the products. I have some simples products under configurables ones. All is working well, configurable products are created with the simple products the "associated products" tab but one last thing still doesn't work : the price of each product.
Actually, Each simple product has its own price and the correct value is well saved in its attribute but in the "super product attribute configuration" panel, the values are empty.
When I fill the price fields manually, it works but it obviously must be done by the script, programmatically.
Here is my function to create the configurable product :
protected function createConfigurableProductFromSimpleProduct($product, $flagshipID)
{
$configurableProduct = $product->duplicate();
$configurableProduct->getResource()->save($configurableProduct);
$configurableProduct= Mage::getModel('catalog/product')->load($configurableProduct->getId());
$configurableProduct->setTypeId(Mage_Catalog_Model_Product_Type::TYPE_CONFIGURABLE)
->setVisibility(Mage_Catalog_Model_Product_Visibility::VISIBILITY_BOTH)
->setSku($flagshipID)
->setDefRef($product_id)
->getTypeInstance()->setUsedProductAttributeIds(array($this->getAttributeId('root_colors'))); //attribute ID of attribute 'root_colors' in my store
$configurableProduct->setName($configurableProduct->getName());
$configurableProduct->setStatus(1);
$configurableProduct->setStockData(array(
'is_in_stock' => 1
));
$configurableProduct->setUrlKey($configurableProduct->getName());
$configurableProduct->save();
return $configurableProduct;
}
And here is the code for linking simple products to this configurable product :
protected function linkSimpleProductsToConfigurableProduct($simpleProducts, $configurableProduct)
{
$configurableProductsData = array();
foreach ($simpleProducts as $_product) {
$_product->setVisibility(Mage_Catalog_Model_Product_Visibility::VISIBILITY_NOT_VISIBLE);
$_product->getResource()->save($_product);
$configurableProductsData[$_product->getId()] = array( //[id] = id of a simple product associated with this configurable
'0' => array(
'label' => $this->getAttributeRawValue($this->getAttributeId('root_colors'), $_product->getRoot()), //attribute label
'attribute_id' => $this->getAttributeId('root_colors'), //attribute ID of discriminator attribute in my store
'value_index' => $_product->getColor(),
'pricing_value' => $_product->getPrice(),
'is_percent' => '0'
)
);
}
$configurableAttributesData = $configurableProduct->getTypeInstance()->getConfigurableAttributesAsArray();
$configurableProduct->setCanSaveConfigurableAttributes(true);
$configurableProduct->setConfigurableAttributesData($configurableAttributesData);
$configurableProduct->setConfigurableProductsData($configurableProductsData);
var_dump('saving configurable product after having link some simple products');
$configurableProduct->save();
}
Any help is welcome, thank you !
Hy all! After reading thousand of answer i solved it!
$cProduct->setCanSaveConfigurableAttributes(true);
$cProduct->setCanSaveCustomOptions(true);
$cProductTypeInstance = $cProduct->getTypeInstance();
$cProductTypeInstance->setUsedProductAttributeIds(array($attribute_id));
$attributes_array = $cProductTypeInstance->getConfigurableAttributesAsArray();
foreach($attributes_array as $key => $attribute_array) {
$attributes_array[$key]['use_default'] = 0;
$attributes_array[$key]['position'] = 0;
if (isset($attribute_array['frontend_label'])) {
$attributes_array[$key]['label'] = $attribute_array['frontend_label'];
} else {
$attributes_array[$key]['label'] = $attribute_array['attribute_code'];
}
}
$dataArray = array();
foreach ($simpleProducts as $simpleArray) {
$dataArray[$simpleArray['id']] = array(
"label" => $simpleArray['label'],
"attribute_id" => $simpleArray['attr_id'],
"value_index" => $simpleArray['value'],
"is_percent" => '0',
"pricing_value" => $simpleArray['price']
);
}
// MAIN MOD! Here i prepare an array for attributesData.
$valuesArray = array();
foreach($dataArray as $data){
$valuesArray[] = $data;
}
// MAIN MOD!
// this is not the best, but in my case I've only one attribute.
$attributes_array[0]['values'] = $valuesArray;
$cProduct->setConfigurableProductsData($dataArray);
$cProduct->setConfigurableAttributesData($attributes_array);
I dont post all the codes, but i see that with these little modification it solve the problem!
I'm having the exact same issue and suspect this is due to something in Magento 1.9. I've tried a couple of code workarounds, but none of them work yet. Hmm, same bummer as you are suffering from.
Instead of providing further comments, I'll just provide an answer instead ;)
I digged into this a little bit deeper and found that the setConfigurableProductsData() call is designed to set product information, but the flag "pricing_value" simply does not work (at least it doesn't work in Magento 1.9). The call is only used to identify the Simple Products that are part of this Configurable Product. Instead the setConfigurableAttributesData() call needs to be used to define the relationship between Simple Products and pricing.
The following code worked for me: It moves the definition of specific option values (identified with value_index) with specific prices (pricing_value) as part of the attribute-data and not the product-data.
$configurableAttributeSizeValues = array();
foreach($simpleProducts as $simpleProduct) {
$configurableAttributeSizeValues[] = array(
'label' => $simpleProduct->getAttributeText('size'),
'value_index' => $simpleProduct->getSize(),
'is_percent' => false,
'pricing_value' => $simpleProduct->getPrice(),
);
}
$configurableAttributeSize = array(
'id' => null,
'label' => 'Size',
'frontend_label' => 'Size',
'attribute_id' => $attribute->getId(),
'attribute_code' => 'size',
'values' => $configurableAttributeSizeValues,
'position' => 0,
);
$configurableAttributesData = array($configurableAttributeSize);
$configurableProductsIds = array();
foreach($simpleProducts as $simpleProduct) {
$configurableProductsIds[$simpleProduct->getId()] = $simpleProduct->getId();
}
$product->setConfigurableProductsData($configurableProductsIds);
$product->setConfigurableAttributesData($configurableAttributesData);
$product->setCanSaveConfigurableAttributes(true);
Before this code is put to use, I've loaded a product-collection called $simpleProducts and a single attribute $attribute. If you want to load multiple attributes to the same product, you can by adding them to the $configurableAttributesData array.
The original $configurableProductsData that you created in your code can still be used, however because most of its purpose is moved to the attributes-array instead, you can also just create a simple array of Simple Product IDs instead. That's what I did with $configurableProductsIds.
Hope this helps you out as well.
Here is what worked for me (some code repeated for clarity). Both setConfigurableProductsData and setConfigurableAttributesData need to be used for pricing_value to be saved.
$configurableAttributesData = $product->getTypeInstance()->getConfigurableAttributesAsArray();
$product->getTypeInstance()->setUsedProductAttributeIds(array($attribute->getId()));
$product->setCanSaveConfigurableAttributes(true);
$configurableProductsData[$childId] = array(
$childProductId => array(
'label' => $childProduct->getAttributeText($attribute->getAttributeCode()),
'attribute_id' => $attribute->getId(),
'value_index' => $optionId,
'is_percent' => false,
'pricing_value' => $childProduct->getPrice()
)
);
$configurableAttributesData[0]['values'][] = array(
'label' => $childProduct->getAttributeText($attribute->getAttributeCode()),
'attribute_id' => $attribute->getId(),
'value_index' => $optionId,
'is_percent' => false,
'pricing_value' => $childProduct->getPrice()
);
$configurableAttributesData[0]['attribute_id'] = $attribute->getId();
$product->setConfigurableProductsData($configurableProductsData);
$product->setConfigurableAttributesData($configurableAttributesData);
$product->save();

Symfony 1.4 sfWidgetFormInputFileEditable customization

I am using the sfWidgetFormInputFileEditable widget for my users to upload images.
I would like to see if there's a way to alter the way it works be default. When a user is adding a "new" object, I would like for it to show a generic picture, and when it's an "edit" then it can show the existing pic. I tried writing a PHP conditional statement but that's not working for me because when it's a "new" item I can't pull the parameter "getPicture1" because it doesn't exist.
My widget currently:
$this->widgetSchema['picture1'] = new sfWidgetFormInputFileEditable(array(
'label' => ' ',
'file_src' => '/uploads/car/'.$this->getObject()->getPicture1(),
'is_image' => true,
'edit_mode' => true,
'template' => '<div>%file%<br />%input%</div>',
));
You have two options (the second one is more easy).
First option: create your own sfWidgetFormInputFileEditable and extends the original.
In a file lib/widget/myWidgetFormInputFileEditable.class.php:
class myWidgetFormInputFileEditable extends sfWidgetFormInputFileEditable
{
protected function getFileAsTag($attributes)
{
if ($this->getOption('is_image'))
{
if (false !== $src = $this->getOption('file_src'))
{
// check if the given src is empty of image (like check if it has a .jpg at the end)
if ('/uploads/car/' === $src)
{
$src = '/uploads/car/default_image.jpg';
}
$this->renderTag('img', array_merge(array('src' => $src), $attributes))
}
}
else
{
return $this->getOption('file_src');
}
}
}
Then you need to call it:
$this->widgetSchema['picture1'] = new myWidgetFormInputFileEditable(array(
'label' => ' ',
'file_src' => '/uploads/car/'.$this->getObject()->getPicture1(),
'is_image' => true,
'edit_mode' => true,
'template' => '<div>%file%<br />%input%</div>',
));
Second option: check if the object is new then use the default image.
$file_src = $this->getObject()->getPicture1();
if ($this->getObject()->isNew())
{
$file_src = 'default_image.jpg';
}
$this->widgetSchema['picture1'] = new sfWidgetFormInputFileEditable(array(
'label' => ' ',
'file_src' => '/uploads/car/'.$file_src,
'is_image' => true,
'edit_mode' => true,
'template' => '<div>%file%<br />%input%</div>',
));

List node comments using existing comment.tpl.php from custom Drupal module

I have created a module which gets the comments from the nodes which the user has specified as 'favourites'. So I'm not trying to output all comments from all nodes like the recent comments block do, but just the ones from nodes specified as 'favourites'.
The queries all work, I've tested this by printing values from the different objects. So I've got the whole comment object for each comment and the corresponding node object. I've been able to create lists of the cid, nid, comment text etc. and output these with
$block['content'] = theme('item_list', array('items' => $items));
but how would I go about rendering the comment objects I've got in my module in the same layout/design as I have on my node pages? The comments on my node pages are rendered with the comment.tpl.php file which I set up with my own layout/design and I'd like my module to render these comments the same way.
So this is my hook_block_view() implementation which I believe is the correct way for output from a module:
function jf_comment_feed_block_view($delta = '') {
switch($delta){
case 'jf_comment_feed':
$block['subject'] = t('Comment feed');
if(user_access('access content')){
// Get users favourite locations
$loc_result = jf_comment_feed_locations();
$fav_loc = array();
foreach ($loc_result as $loc) {
$fav_loc[] = array(
'data' => $loc->nid,
);
}
if (empty($fav_loc)) { //No content in the last week.
$block['content'] = t('No favourite locations added.
To see what goes on at your favourite locations add locations to
+My Locations and the posts from those locations will show here.');
} else {
//Use our custom function to retrieve data.
$result = jf_comment_feed_contents($fav_loc);
// ############################################
// Here I need to create my output... I think...
// Previously I rendered my results from the query
// by using this code (this prints the comment id):
// $items = array();
// foreach ($result as $comment){
// $items[] = array(
// 'data' => comment_load($comment->cid),
// );
// }
// ############################################
if (empty($items)) { //No content in the last week.
$block['content'] = t('No posts from last week.');
} else {
// This is the code used to render the
// comment id to the block:
// $block['content'] = theme('item_list', array('items' => $items));
}
}
}
}
return $block;
}
I've also tried with:
$block['content'] = theme('comment_view', $mycomment, $mynode);
$block['content'] = theme('comment', $mycomment, $mynode);
where $mycomment is the comment object and $mynode is the node object. But this breaks the page.
Surely there must be a line of code I'm missing here, but I've now spent two days googling this and had no luck... So thanks for any help with this.
EDIT
#Clive did trigger some ideas and I tried creating my own array based on what the arrays look like on the node page. I got the structure and names for the array with the Devel Themer info module.
This array outputs the comments creators user pic and the date, but I've added a custom field, field_jf_comment, to my comments and this isn't showing, although I can see the information in the array with Devel. I don't use the standard out-of-the-box comment field because I wanted a textfield and not a scalable textarea for the input. A design decision.
Now obviously this isn't ideal as I set most of the values manually. This works for my current project, but would be cool if the module was a bit more generic so other people could use it too. When I click on a individual comment on my node page with Devel Themer info I get an array which has elements, the user object and array items such as db_is_active, is_admin among other things. If I could somehow recreate this array and then set this array to $block['content'] I believe this would work.
Here's the implementation of the array:
foreach ($result as $comment) {
$items[] = array(
'#entity_type' => 'comment',
'#bundle' => 'comment_node_location',
'#theme' => 'comment__node_location',
'#comment' => comment_load($comment->cid, FALSE),
'#node' => node_load($comment->nid),
'#view_mode' => 'full',
'field_jf_comment' => array(
'#theme' => 'field',
'#title' => 'Title',
'#access' => TRUE,
'#label_display' => 'above',
'#view_mode' => 'full',
'#language' => 'und',
'#field_name' => 'field_jf_comment',
'#field_type' => 'text',
'#entity_type' => 'comment',
'#bundle' => 'comment_node_location',
'#items' => array(
'0' => array(
// This isn't working and gives an error saying:
// Notice: Undefined property: stdClass::$field_jf_comment in
// jf_comment_feed_block_view()
'value' => $comment->field_jf_comment['und']['0']['value'],
'format' => $comment->field_jf_comment['und']['0']['format'],
'safe_value' => $comment->field_jf_comment['und']['0']['safe_value']
)
)
)
);
}
And I get it rendered with:
$block['content'] = $items;
EDIT
#Clive was right. His code does the same as mine, but in way less code. And with some modifications I managed to get my custom field in there too:
$content = '';
foreach ($items as $item) {
$single_comment = comment_load($item['cid']);
$custom_field = field_attach_view('comment', $single_comment, 'field_jf_comment');
$to_render = array(
'#theme' => 'comment',
'#comment' => $single_comment,
'#node' => node_load($item['nid']),
'field_jf_comment' => $custom_field
);
$content .= render($to_render);
}
$block['content'] = $content;
Now the only thing I'm missing is the links for each comment. The only one I'm using is the Reply to comment. Anyone got any idea of how to get that to show too?
The theme() calls probably break because you're using Drupal 7 but trying to pass the parameters in a Drupal 6 style. If you have a look at the theme_comment() documentation you can see it takes a single $variables parameter which should be an array. Try this:
$content = '';
foreach ($items as $item) {
$to_render = array(
'#theme' => 'comment',
'#comment' => comment_load($item['cid'])
);
$content .= render($to_render);
}
$block['content'] = $content;
The new Drupal 7 theme() syntax takes an array for its second argument. This array will be extracted before calling the template file so each key will become a new php var.
For example array( 'comment' => $mycomment ) will get you a $commentvariable in your template.
Hope this can help.

Problem when I am populating data into selectbox through ajax in Drupal

I have installed drupal 6, added some cck fields in one content type. Added two select box fields.
I am taking the selected value of parent select box and as per that selection passing relates options to next select box field using Ajax. (e.g Country -> State. When user selects country I want to pass state values into next select box.)
But when I am submitting the form it gives the following error:
"An illegal choice has been detected. Please contact the site administrator."
I don't know why it is not taking the ajaxified select box values while saving the node.
Does somebody has the solution on it. Is there any solution to handle this dynamic select options in Drupal.
Thanks in advance.
The same thing I'm working on drupal 7 and its work for me. Below is the code. Hope this help you. What I have done is on selection of car model car variant will change and the data save in the table.
function add_offer_form($form, $formstate) {
$form['add_offer_new_car_model'] = array(
'#type' => 'select',
'#required' => TRUE,
'#options' => $car_model,
'#ajax' => array(
'effect' => 'fade',
'progress' => array('type' => 'none'),
'callback' => 'variant_callback',
'wrapper' => 'replace_variant',
),
);
// Combo box to select new car variant
$form['add_offer_new_car_variant'] = array(
'#type' => 'select',
'#options' => array(),
// The prefix/suffix provide the div that we're replacing, named by #ajax['wrapper'] above.
'#prefix' => '<div id="replace_variant">',
'#suffix' => '</div>',
);
// An AJAX request calls the form builder function for every change.
// We can change how we build the form based on $form_state.
if (!empty($formstate['values']['add_offer_new_car_model'])) {
$model_id = $formstate['values']['add_offer_new_car_model'];
$rows = array();
$result = db_query("SELECT id, variant_name from {va_car_variant} where car_model_id in ($model_id,1) order by variant_name");
while ($data = $result->fetchObject()) {
$id = $data->id;
$rows[$id] = $data->variant_name;
}
$form['add_offer_new_car_variant']['#options'] = $rows;
}
}
////////////////////////////////////////////////////////
///////// FUNCTION FOR AJAX CALL BACK
function variant_callback($form, &$form_state) {
return $form['add_offer_new_car_variant'];
}

Resources