how to programmatically create menu items while creating nodes? - api

I'm creating some nodes programmatically, thus:
foreach ($titles as $t) {
$n = new stdClass();
$n->type = 'myType';
$n->uid = 1;
$n->title = $t;
$menu = array();
$menu['link_title'] = $t;
$menu['menu_name'] = 'primary-links';
// this attempt at placing the menu item in a particular place in the
// menu hierarchy didn't work:
$menu['parent'] = 'primary-links:867';
$menu['depth'] = 3;
$menu['p1'] = '580';
$menu['p2'] = '867';
$n->menu = $menu;
node_save($n);
}
I've got a menu structure like this:
primary-links
Parent 1
Child 1
Child 2
Parent 2
Child 3
I want the new menu items to appear as children of Child 3. I was able to create menu items at the same time as the nodes, and they appeared in the correct menu, but not in the correct place in the hierarchy. What am I missing?

In drupal 7 you need to set also enabled to 1 (see: menu_node_save()):
$node->menu = array(
'link_title' => $node->title,
'menu_name' => 'main-menu',
'plid' => 0,
'enabled' => 1,
);

I think your over complicating it. In the past when I've programmatically created menu items for nodes, I just set the menu_name, link_title, and plid (parent link id), ie:
$menu['link_title'] = $t;
$menu['menu_name'] = 'primary-links';
$menu['plid'] = 867;
The menu module takes over at some point during the call to node_save and does the rest for you.
~Matt

Also had to add
'description' => ''
to the array otherwise I got an error for Drupal 7

Related

Updating Handles for MatrixBlock "Field Layout Fields" in Craft CMS Migrations

After reading this excellent Medium article, I've been stoked on Migrations in CraftCMS. They've been super useful in importing the same changes across the 10 or so developers who work on our site.
When trying to change the handles on individual fields within block types within matrix blocks (whew) via migrations, I came across a hurdle. The field itself can easily have its "handle" attribute updated, but the columns table for that matrix block's content (matrixcontent_xxxxx) do not get updated to reflect any updated handles. The association between the Matrix Block & its associated Matrix Content table only exists in the info column in the field record for that Matrix Block.
If the Matrix Block's field is updated via the CMS, the change is reflected, so it's gotta be somewhere in Craft's source, but it's not apparent through the Craft CMS Class Reference.
Any ideas?
Edited to add the migration snippet:
public function safeUp()
{
// Strings for labels, etc.
$matrix_block_instructions = "instructions";
$block_label = "Video";
$block_handle = "video";
$field_handle = "video_url";
$field_label = "Video URL";
$field_instructions = "Add a YouTube or Facebook video URL.";
// Fetching the MatrixBlock fields used on the entries themselves
$video_gallery_1 = Craft::$app->fields->getFieldByHandle("handle_1");
$video_gallery_2 = Craft::$app->fields->getFieldByHandle("handle_2");
$video_gallery_3 = Craft::$app->fields->getFieldByHandle("handle_3");
$galleries = [$video_gallery_1, $video_gallery_2, $video_gallery_3];
foreach( $galleries as $gallery ) {
// Fetching the record for this specific MatrixBlock field.
$gallery_record = \craft\records\Field::find()->where(
['id' => $gallery->id]
)->one();
// Fetching the record for this specific MatrixBlockType
$gallery_block_id = $gallery->getBlockTypes()[0]->id;
$gallery_block = \craft\records\MatrixBlockType::find()->where(
['id' => $gallery_block_id]
)->one();
// Assigning standard labels for the MatrixBlockType
$gallery_block->name = $block_label;
$gallery_block->handle = $block_handle;
$gallery_block->update();
// Getting the specific ... 1 ... field to edit
$field_group = \craft\records\FieldLayout::find()->where(
['id' => $gallery_block->fieldLayoutId]
)->one();
$field_layout_field = $field_group->fields[0];
$field = $field_layout_field->field;
$field = \craft\records\Field::find()->where(
['id' => $field->id]
)->one();
// Assigning standard labels for the Label
$field->name = $field_label;
$field->handle = $field_handle;
$field->instructions = $field_instructions;
$field->update();
// Updating the MatrixBlock record with new instructions
$gallery_record->refresh();
$gallery_record->instructions = $matrix_block_instructions;
$gallery_record->update();
}
OK, so my apologies if anyone was stoked on figuring this out, but my approach above was kind of a crazy person's approach, but I've found my own solution.
The key here is that I should have been interacting with craft\fields\MatrixBlock and the craft\fields\PlainText objects, not craft\records\Field objects. There's a method within \craft\services\Fields for saving the field that requires a FieldInterface implemented. This is actually the default classes returned, and I was making more work for myself in the code!
Within that foreach loop, this worked out:
// Fetching the record for this specific MatrixBlock field.
$gallery->instructions = $matrix_block_instructions;
// Update the MatrixBlockType
$gallery_block_id = $gallery->getBlockTypes()[0]->id;
$gallery_block = \craft\records\MatrixBlockType::find()->where(
['id' => $gallery_block_id]
)->one();
$gallery_block->name = $block_label;
$gallery_block->handle = $block_handle;
$gallery_block->update();
// Update the actual field.
$field = $gallery->blockTypeFields[0];
$field->name = $field_label;
$field->handle = $field_handle;
$field->instructions = $field_instructions;
Craft::$app->getFields()->saveField( $field );
Craft::$app->getFields()->saveField( $gallery );
Thanks for looking at this, and sorry for being a crazy person.

Need to display first n items from each category in Razor using Umbraco

I am new to Umbraco. I have a list of items in a content named Videos. Each item have a specific category. I need to retrieve 'n' number of items from each category. Some one please help. Also am using MixItUp jquery plugin to display the items.
// this will bring up all items from the list
var items = Umbraco.TypedContent(Model.Content.Id).Children.Where(x => x.DocumentTypeAlias == "videoItem" && x.IsVisible());
// Here am trying to bring 5 items under category "Testimonial"
var allItems = items.Where(x => x.GetPropertyValue("category") == "Testimonial").Take(5);
But I didn't found any output. Please help.
Your second line of code should read:
var allItems = items
.Where(x => x.GetPropertyValue<string>("category") == "Testimonial")
.Take(5);
Rather than simply cast the result to string, this will try and convert the object to the desired type if it isn't already - see here.
If you're using the new ModelsBuilder (which is awesome) you also have the option of strongly typing the whole process.
var items = Model.Content.Children<VideoItem>().Where(x => x.IsVisible());
var allItems = items.Where(x => x.Category == "Testimonial").Take(5);

How do I iterate through a list of items in a Ext.dataview.List

I'm currently trying to find out how to set items to be selected on a list in ST2.
I've found the following:
l.select(0, true);
l.select(1, true);
which would select the first 2 items on my list. But the data coming from the server is in csv format string with the ids of the items in the list to be selected.
e.g. "4, 10, 15"
So I currently have this code at the moment.
doSetSelectedValues = function(values, scope) {
var l = scope.getComponent("mylist");
var toSet = values.split(",");
// loop through items in list
// if item in list has 'id' property matching whatever is in the toSet array then select it.
}
The problem is I can't seem to find a way of iterating over the items in the list and then inspect the "id" property of the item to see if it matches with the item in the array.
l.getItems()
Doesn't seem to return an array of items. The list is populated via a store with the "id" & "itemdesc" properties. I just want to be able to select those items from a csv string. I've scoured the Api on this and I can't seem to find a way of iterating over the items in the list and being able to inspect its backing data.
the Ext.List's items are not the items you are looking for. The items under the Ext.List object are those:
Ext.create('Ext.List', {
fullscreen: true,
itemTpl: '{title}',
store: theStore,
**items: [item1, item2]**
});
Granted, usually an Ext.List doesn't have items like these. What you are looking for are the Ext.Store items. The Ext.Store items are the exact same items in the same order as presented in the Ext.List.
To iterate over those, and select the corresponding items in the list, do the following:
var s = l.getStore();
var itemIndicesToSelect = [];
for (var i = 0 ; i < s.data.items.length ; i++){
if (arrayContainsValue(toSet, s.data.items[i].data.id)){
itemIndicesToSelect.push(i);
}
}
for (var i = 0 ; i < itemIndicesToSelect.length ; i++){
l.selectRange(itemIndicesToSelect[i], itemIndicesToSelect[i], true);
}
You would have to implement the function arrayContainsValue (one possible solution).
doSetSelectedValues = function(values, scope) {
var l = scope.getComponent("mylist"),
store = l.getStore(),
toSet = values.split(",");
Ext.each(toSet, function(id){
l.select(store.getById(id), true);
});
}

Refresh a dijit.form.Select

First, you have to know that I am developing my project with Struts (J2EE
Here is my problem :
I have 2 dijit.form.Select widgets in my page, and those Select are filled with the same list (returned by a Java class).
When I select an option in my 1st "Select widget", I would like to update my 2nd Select widget, and disable the selected options from my 1st widget (to prevent users to select the same item twice).
I succeed doing this (I'll show you my code later), but my problem is that when I open my 2nd list, even once, it will never be refreshed again. So I can play a long time with my 1st Select, and choose many other options, the only option disabled in my 2nd list is the first I've selected.
Here is my JS Code :
function removeSelectedOption(){
var list1 = dijit.byId("codeModif1");
var list2 = dijit.byId("codeModif2");
var list1SelectedOptionValue = list1.get("value");
if(list1SelectedOptionValue!= null){
list2.reset();
for(var i = 0; i < myListSize; i++){
// If the value of the current option = my selected option from list1
if(liste2.getOptions(i).value == list1SelectedOptionValue){
list2.getOptions(i).disabled = true;
} else {
list2.getOptions(i).disabled = false;
}
}
}
Thanks for your help
Regards
I think you have to reset() the Select after you've updated its options' properties. Something like:
function removeSelectedOption(value)
{
var list2 = dijit.byId("codeModif2"),
prev = list2.get('value');
for(var i = 0; i < myListSize; i++)
{
var opt = myList[i];
opt.disabled = opt.value === value;
list2.updateOption(opt);
}
list2.reset();
// Set selection again, unless it was the newly disabled one.
if(prev !== value) list2.set('value', prev);
};
(I'm assuming you have a myList containing the possible options here, and the accompanying myListSize.)

Recipe for adding Drupal node records

I am looking for a recipe for adding Drupal node records.
I have identified three tables.
node_revisions
nid=249 - vid + 1?
vid=248 - auto-increment
node:
nid=250 - vid + 1?
vid=249 - auto-increment
content_type_my_content
vid=248 - from node_revisions table?
nid=249 - from node table?
Am I on right track?
Is there some helper functions for this?
If you are looking to programatically create nodes, use the Drupal API.
Start by creating a $node object. Fill in title, type, status, body, plus any CCK fields. At the end, call node_save($node);.
node_save will save your node object and do the necessary database work.
Check this out:
http://api.drupal.org/api/function/node_save/6
http://mediumexposure.com/how-build-node-drupal-programmatically/
The easiest way to see what each type of content type has as fields is to create a node (for example, Page), then use var_dump() to see the node's contents. That will show you every field you will need to use in your node object creation script.
Some folks will say you should create a form array, and call drupal_execute() on it so validation is performed before it's saved to the database. Either way is fine.
Kevin - With your help I have made good progress.
Node and CCK fields are now being populated.
Location (long/lat) is populated but not showing up on View screen.
Checkboxes are not being populated.
global $user;
$newnode = new stdClass();
$newnode->title = 'New node title';
$newnode->body = "this is a new node, created by import function";
$newnode->uid = $user->uid;
$newnode->type = 'items';
$newnode->status = 1;
$newnode->promote = 0;
// CCK fields
$newnode->field_myfield1[0]['value'] = 'test 1';
$newnode->field_myfield2[0]['value'] = 'test 2';
$newnode->field_mycheckbox[0]['value'] = 1;
// longitude, lalitude
// $newnode->locations[0]['lid'] = ?;
$newnode->locations[0]['street'] = 'xx';
$newnode->locations[0]['city'] = 'xx';
$newnode->locations[0]['province'] = 'xx';
$newnode->locations[0]['postal_code'] = 'xx';
$newnode->locations[0]['latitude'] = 0;
$newnode->locations[0]['longitude'] = 0;
$newnode = node_submit($newnode);
node_save($newnode);
content_insert($newnode);
OK. here is the full recipe. Drupal does the rest automagically.
global $user;
// Node fields
$newnode = new stdClass();
$newnode->title = $data[0];
$newnode->body = $data[1];
$newnode->uid = $user->uid;
$newnode->type = 'mytype';
$newnode->status = 1;
$newnode->promote = 0;
// CCK fields
$newnode->field_myfield1[0]['value'] = $something;
$newnode->field_myfield2[0]['value'] = $something;
$newnode->field_my_checkbox[0]['value'] = $something;
// longitude, latitude
$newnode->field_loc_latitude[0]['street'] = $something;
$newnode->field_loc_latitude[0]['city'] = $something;
$newnode->field_loc_latitude[0]['province'] = $something;
$newnode->field_loc_latitude[0]['postal_code'] = $something;
$newnode->field_loc_latitude[0]['latitude'] = '';
$newnode->field_loc_latitude[0]['longitude'] = '';
$newnode = node_submit($newnode);
node_save($newnode);
content_insert($newnode);