I am creating custom CMS fields in SilverStripe 4 and they are being built with the label in a left column and the editor in the right column.
See Image:
How do I stack the label and editor like the default content editor and label seen in the picture?
So I finally found the answer while searching for something else. I'll answer my own question in case someone is looking for the same thing in the future as finding answers for Silverstripe questions is hard.
In the doc's it says to do this for an additional WYSIWYG editor
return new FieldList([
new HTMLEditorField('OtherContent', 'Other content', $this->OtherContent, 'myConfig')
]);
We are going to break it up into components to give us more control
$fields = parent::getCMSFields();
//create a $fields variable that will hold the new fields
$jobDescriptionField = HTMLEditorField::create('JobDescription', 'JobDescription');
//create the actual field in it's own variable
$fields->addFieldToTab('Root.Main', $jobDescriptionField , 'Content');
//add the new field to our fields and tell it to appear above the default 'Content' editor
If we stop here and return $fields, we will get the Label floated to the left and content editor floated to the right. It will be squished even on a full screen. Not good.
So we need to add a class provided by silverstripe called "stacked"
$jobDescriptionField->addExtraClass('stacked');
So the full code looks like:
public function getCMSFields(){
$fields = parent::getCMSFields();
$jobDescriptionField = HTMLEditorField::create('JobDescription', 'JobDescription');
$fields->addFieldToTab('Root.Main', $jobDescriptionField , 'Content');
$jobDescriptionField->addExtraClass('stacked');
return $fields;
}
Related
I am trying to add a bunch of different empty paragraphs of different types, to a entity reference revisions field, everytime a node of a certain content type is created.
I DON'T want to use the contrib module "default paragraphs" for this, because I need to use a certain form widget here, and default paragraphs is also achieved by a widget.
What I tried so far:
function myModule_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id){
$paragraph = \Drupal\paragraphs\Entity\Paragraph::create([
'type' => 'tab_features'
]);
$paragraph->save();
$form['field_tabs']['widget'][0]['target_id']=$paragraph->id();
$form['field_tabs']['widget'][0]['target_revision_id']=$paragraph->getRevisionId();
return $form;
}
$field_tabs is my entity reference revisions field.
'tab_features' is the paragraphs type I want to add.
I guess there should be a method that can be used in the form or form widget to add a paragraph to the form, like someone already clicked the button to add it. I want to avoid to actually trigger this via Javascript if possible. Anybody knows how to do this in form_alter?
In a project I'm working on, we have done something like this:
//get the body field
$field = $entity->get('field_em_p_body');
$paragraph = Paragraph::create([
'type' => 'em_section', // Paragraph type.
]);
$paragraph->isNew();
$paragraph->set('YOUR_FIELD', 'SOMETHING');
$field->appendItem($paragraph);
I am trying to create a back-end interface for silverstripe that gives the CMS user the option to choose between a set of Post Types (like tumblr) in Silverstripe3. So they can choose to create a News Post, Video Post, Gallery Post, etc.
I initially started off giving all Posts the necessary fields for each Type and adding an enum field that allowed the user to choose the Post Type. I then used the forTemplate method to set the template dependent upon which Post Type was chosen.
class Post extends DataObject {
static $db = array(
'Title' => 'Varchar(255),
'Entry' => 'HTMLText',
'Type' => 'enum('Video, Photo, Gallery, Music')
);
static $many_many = array(
'Videos' => 'SiteVideo',
'Photos' => 'SitePhoto,
'Songs' => 'SiteMp3'
);
public function forTemplate() {
switch ($this->Type) {
case 'Video':
return $this->renderWith('VideoPost');
break;
case 'Photo':
return $this->renderWith('ImagePost');
break;
etc...
}
function getCMSFields($params=null) {
$fields = parent::getCMSFields($params);
...
$videosField = new GridField(
'Videos',
'Videos',
$this->Videos()->sort('SortOrder'),
$gridFieldConfig
);
$fields->addFieldToTab('Root.Videos', $photosField);
$photosField = new GridField(
'Photos',
'Photos',
$this->Photos()->sort('SortOrder'),
$gridFieldConfig
);
$fields->addFieldToTab('Root.Videos', $photosField);
return $fields;
}
}
I would rather the user be able to choose the Post Type in the backend and only the appropriate tabs show up. So if you choose Video, only the Video GridField tab would show up. If you choose Photo Type only the Photo's GridField would show.Then I would like to be able to call something like
public function PostList() {
Posts::get()
}
and be able to output all PostTypes sorted by date.
Does anyone know how this might be accomplished? Thanks.
Well the first part can be accomplished using javascript. Check out this tutorial and the docs let me know if you have questions on it.
The second part would be trickier but I think you could do something with the page controller. Include a method that outputs a different template based on the enum value but you would have to set links somewhere.
I managed this with DataObjectManager in 2.4.7 as I had numerous DataObjects and all were included in one page but I'm not sure if that is feasible in SS3.
return $this->renderWith(array('CustomTemplate'));
This line of code will output the page using a different template. You need to include it in a method and then call that method when the appropriate link is clicked.
I have added new Sub Tabs Under The Settings->Main Tab in The Silverstripe 3.0 Back-End using the following code:
public function updateCMSFields(FieldList $fields) {
$fields->addFieldToTab('Root.Main', new TabSet('Tab1'),'Theme');
$fields->addFieldToTab('Root.Main.Tab1', new Tab('SubTab1',new HTMLEditorField("Text", "Text")));
$fields->addFieldToTab('Root.Main.Tab1', new Tab('SubTab2', new DropdownField("DropDown1","DropDown",
array(
'-1' => 'Option1',
'1' => 'Option2',
))));
}
But The Main Tab already has a field named theme under it which is generated by default. I need to keep this field separated from the tabs which I have added...maybe creating a new sub-tab for it...Please tell me how I can achieve this...Currently it appears under every tab...
You could remove the default 'Theme' field altogether if you'd like.
Put this line before all the code you posted:
$fields->removeByName('Theme');
can I automatically add a menu item when I add a node to the page in Drupal?
In other words, can I associate a menu parent with a node content-type, and then automatically add the children if new nodes are added ?
thanks
You can do it with Rules on Drupal 7. This module: http://drupal.org/project/menu_rules adds some actions to rules. One of them is to create a menu item for a node. You select:
Event: Create a node | Update a node
Condition: Content type is "your content type"
Action: Update a menu item for node (there is a checkbox to create the menu item if it doesnt exist)
There's also the Menu Position module that allows to put content under specific menu entries, depending on their content type, their language and taxonomy. It also has a small API to add other criteria.
Yes.
I am sure there is a module do to something like that, but you could also create your own.
There are two ways you could go about it.
You could use hook_menu() to query for the items you want and return the correct menu structure. You would need to also make sure the menu cache is rebuilt on a node save using hook_nodeapi().
See henricks' comments below about why this is a bad idea
Alternitivly you could use hook_nodeapi() to add custom menu items with menu_link_save().
Edit
hook_menu should return an array of menu items, often these are pretty static however there is nothing wrong with these arrays being dynamically generated.
So you can query the node table to get a list of nodes you want, loop through these items and dynamically create an array which contains the correct menu items.
very roughly:
function example_menu() {
$result = db_query('select * from node where ...'); // put in your own select items and where clause
$menu = array();
while ($row = db_fetch_object($result)) {
$menu['my_path/' . $row->nid;] = array(
// See hook menu docs for what to put here.
);
}
return $menu;
}
You should take a look at the Auto Menu module - while the Drupal 6 version is still a dev release, it might cover your needs. If not, you can take it as an example of how to use menu_link_save() to create your own solution.
I would also go for a menu_link_save() call. Together with the Rules module, you can set up an action whenever a new node is saved, to create an appropriate menu item automatically.
You might want to have a look at the tutorial I wrote some time ago, which deals with programatically creating menu items using menu_link_save() and Rules: http://jan.tomka.name/blog/programmatically-creating-menu-items-drupal
Here is case where you can do this....
A node campaign creating menu item 'CAMPAIGN 001' when it is created. Using default_menu_link
Now another content type, 'Sub Campaign' creating a node, using campaign as EntityRef so its menu item should be under the Menu Item of campaign created earlier.
function mymodule_node_insert($node) {
if ($node->type == 'sub-campaign') {
if (isset($node->field_reference_campaign['und'][0]['target_id'])) {
$campaign_node_id = $node->field_photo_album_campaign['und'][0]['target_id'];
$campaign_loaded = node_load($campaign_node_id);
// Get menu link id for the campaign node.
$campaign_node_id_mlid = custom_node_mlid($campaign_node_id);
$campaign_loaded_title = strtolower(str_replace(' ', "-", $campaign_loaded->title));
$campaign_loaded_title_link_path = 'campaign/' . $campaign_loaded_title . '/photo-albums';
//I will query if it exist or not, if not then will create a sub menu item.
$link_exist = db_query("SELECT * FROM {menu_links} WHERE link_path = :link_path", array(':link_path' => $campaign_loaded_title_link_path))->fetchField();
dsm($link_exist);
if (!$link_exist) {
// Create menu item under campaign.
custom_create_menu_item($campaign_loaded_title_link_path, 'photo-albums', $campaign_node_id_mlid);
//watchdog('glue_site - Menu Item', 'Link Created');
}
else {
//dsm('Link Exist.');
watchdog('glue_site - Menu Item', 'Link Already Exist');
}
}
}
if ($node->type == 'campaign') {
}
}
Then a custom function to create menu item
function custom_create_menu_item($campaign_loaded_title_link_path, $type, $plid) {
switch ($type) {
case 'photo-albums':
$item = array(
'link_path' => $campaign_loaded_title_link_path,
// If changing the title here, change it in template.php as well.
'link_title' => 'Sub Campaign',
'menu_name' => 'menu-campaign-menu', // Menu machine name, for example: main-menu
'weight' => 0,
'plid' => $plid, // Parent menu item, 0 if menu item is on top level
'module' => 'menu',
'router_path' => 'campaign/%/sub-campaign',
'customized' => '1',
);
menu_link_save($item);
menu_cache_clear_all();
watchdog('glue_site - Menu Item', 'Link Created');
break;
}
}
To get the mlid of parent node. Campaign node...
function custom_node_mlid($nid) {
// Require menu node module.
$arr = menu_node_get_links($nid);
$mlid = array_keys($arr);
return $mlid[0];
}
For this you need menu_node
This is a simple problem that unfortunately the Drupal community has decided it wants to make complicated. Forget about all the hacky solutions with rules and hooks. There are two modules, depending on whether you're on Drupal 6 or Drupal 7, that solve the problem very elegantly. I advise against actually creating menu entries. Instead the two modules below dynamically render the nodes in the menu, so that your menu editor doesn't get filled with thousands of nodes. Then, for example, if you decide you want all the blog posts to be moved from [Our Blog] to [About Us]->[News] it's just a mater of changing one setting. No updating thousands of nodes.
D6 Menu Trails
D7 Menu Position
It looks like there's a Drupal module that does this: Auto Menu. Some more details about this module (from its project page):
The Auto Menu module automatically generates menu entries on node creation/edition. Parent menu item can be specified on a per content type basis.
This module acts when the menu section of a node is left empty only. So, users can still organize menus manually. Moreover, default setting for content types is to not create menu items automatically.
Menu Views is an interesting module for Drupal 7 to automatically generate menu links. It allows you to use the power of Views to create menu links and can be used out-of-the-box in combination with modules such as Superfish and Nice Menus.
(PS: my reputation is not high enough to provide more than two links, therefore I have marked the other modules bold instead of providing hyperlinks)
I have a View with an exposed form . I am trying to a few things on it. Ideally I would like to have a dropdown that fires the form with no button. If that is not possible then I would like to have the button text something different than apply.
I hacked it for now and change views_form in views.module but that does not seem like the right way to do it. I only have one exposed form right now, but what if I add more?
Please see http://www.wiredvillage.ca/News for my example.
I am poking around drupal.org and seeing others with the same problem but no solutions so far. Not sure where the best place to get Drupal help is.
Here is the change I made so far:
function views_exposed_form(&$form_state) {
// Make sure that we validate because this form might be submitted
// multiple times per page.
$form_state['must_validate'] = TRUE;
$view = &$form_state['view'];
$display = &$form_state['display'];
$form_state['input'] = $view->get_exposed_input();
// Let form plugins know this is for exposed widgets.
$form_state['exposed'] = TRUE;
$form['#info'] = array();
if (!variable_get('clean_url', FALSE)) {
$form['q'] = array(
'#type' => 'hidden',
'#value' => $view->get_url(),
);
}
// Go through each filter and let it generate its info.
foreach ($view->filter as $id => $filter) {
$view->filter[$id]->exposed_form($form, $form_state);
if ($info = $view->filter[$id]->exposed_info()) {
$form['#info']['filter-' . $id] = $info;
}
}
// I CHANGED The VALUE OF THIS SUBMIT BUTTON TO GO
$form['submit'] = array(
'#name' => '', // prevent from showing up in $_GET.
'#type' => 'submit',
'#value' => t('go'),
);
$form['#action'] = url($view->get_url());
$form['#theme'] = views_theme_functions('views_exposed_form', $view, $display);
$form['#id'] = views_css_safe('views_exposed_form-' . check_plain($view->name) . '-' . check_plain($display->id));
// $form['#attributes']['class'] = array('views-exposed-form');
// If using AJAX, we need the form plugin.
if ($view->use_ajax) {
drupal_add_js('misc/jquery.form.js');
}
views_add_js('dependent');
return $form;
}
Or, you could use a preprocess function to alter the form even before it is build. I wanted to change the text on the button, so I did this:
function MYTHEME_preprocess_views_exposed_form(&$vars, $hook) {
// only alter the jobs search exposed filter form
if ($vars['form']['#id'] == 'views-exposed-form-jobs-search-page-1') {
// Change the text on the submit button
$vars['form']['submit']['#value'] = t('Search');
// Rebuild the rendered version (submit button, rest remains unchanged)
unset($vars['form']['submit']['#printed']);
$vars['button'] = drupal_render($vars['form']['submit']);
}
}
If you want the drop-down to fire, I'd use JavaScript instead of hacking the module as Eaton suggests.
Basically, you can modify the text with hook_form_alter as Eaton suggests, then use in the same hook_form_alter, add a call to drupal_add_js with your custom JS which hides the button and submits the form on the onChange handler of the select drop-down. You want that submit button there for those 10% of users for whom the JS fails.
Both of the above are fine but I found out that altering the form might not always lead to desirable results, mainly because exposed filters are themed using a specifc theme template. The proper way of changing the theme would be to override the views-exposed-form.tpl file in your theme's folder. Bear in mind that this will apply to all exposed filter forms, to theme a specific one, you will need to use a different name for that filename, like:
views-exposed-form--TITLE--DISPLAY.tpl.php
views-exposed-form--TITLE.tpl.php
and some others, you can check the Theme: Information section of your views for template naming conventions.
This module provides an auto-submit among other things http://drupal.org/project/views_hacks
This module is great to improving exposed filters http://drupal.org/project/better_exposed_filters
You should be able to use hook_form_alter() (http://api.drupal.org/api/function/hook_form_alter) to change the form as it's built, modifying the fields in question when that particular view is being displayed. You can nuke the submit button, add a #theme function that calls the drupal_add_js() function, and so on.
As long as the GET params come in the way views expect them, everything will work fine -- it was designed that way to allow bookmarking of pages with exposed filter settings, etc. The important part is to make sure you're doing the form mangling in your own module's hook_form_alter() function, so that it won't make other views driven stuff choke.