Limiting fields to certain users - silverstripe

Using SilverStripe 2.4.7.
I've done some searching but I can't seem to be able to find an answer to this. I want to include a checkbox on a popup window in dataobjectmanager but not for every user.
I have two separate pages, one for one user and another for the other, and I only want the checkbox on one. I thought an if statement would suffice, quick and simple right?
public function getCMSFields()
{
$categories = array("Morning","Afternoon", "Evening", "Night");
return new FieldSet(
new TextField('Title'),
new DatePickerField('Date', 'Date'),
new ImageField('Photo', 'Photo'),
new MoneyField('AdultPrice', 'Adult Price'),
new MoneyField('ChildPrice', 'Child Price'),
new DropdownField('Category', 'Choose a Category', $categories)
);
This is my attempt at the if statement approach
if($this->ClassName == 'Movie'){
$films= DataObject::get('Films');
if (!empty($films)) {
// create an array('ID'=>'Name')
$map = $films->toDropdownMap('ID', 'Name');
$fieldset->push(new CheckboxSetField(
$name = "Films",
$title = "Select Films",
$source = $map
));
}
}
Basically this works if I use it within getCMSFields_forPopup, but not in just getCMSFields, but changes my checkboxsetfield to a dropdown.
Edit
I have found that my approach would not work due to the fact that the DOM Popup cannot have the classname of the page which contains the DOM (DataObjectManager). This is a simple inheritance issue and I can't believe I didn't see it before. See the answer below for details of how I solved my original query.

In the end the answer was quite simple. I did the following and I hope someone finds this useful.
Create the checkboxset on the page as normal
if(Permission::check("ADMIN")){
if (! empty($values))
{
$checkBox = new CheckboxSetField(
$name = "Values",
$title = "Select Value",
$source = $values
);
$fieldset->push($checkBox);
}
}
The if statement with Permission::check("ADMIN") checks if the admin is logged in and only shows the checkboxset if they are.
You will also need to include the $fieldset->push within this method to add it to the cms. The ADMIN can be changed to any of your user groups which you created in the security panel so this is an adaptable approach.
I found this the best way for me but if someone can improve on it/offer a better solution I'd love to hear about it.

Related

How to use Silverstripe 3 beta UploadField

I am trying to use a UploadField on frontend for user to upload their company logo.
There isn't much documentation on UploadField yet. And I have tried it but no luck so far.
Can anyone guide me on how to use it?
This is a little old, but if anyone else stumbles upon this like I did.
UploadField does work frontend. I haven't been able to save into a many_many relationship using the saveInto function. But the biggest thing I missed was the DataObject/Page needs to exist first, as in it needs to be saved before you can attach a related object like an image.
static $has_one = array(
"Photo" => "Image"
);
$fields = new FieldList(
new UploadField( 'Photo', 'Upload' )
);
function saveForm( $data, $form ) {
$object = new DataObject();
// for a new object write before saveinto
$object->write();
$form->saveInto($object);
$object->write();
Director::redirectBack();
}
using ss 3.0.1
Alternatively rather than using the saveinto function you can manually loop over the parameters and attach them on the object yourself for many_many images.
The upload field checks for permissions via the can*() methods in the object.
In order to allow front end editing - you may have to overload File::canEdit (or Image::canEdit) in your custom object to handle this.

Drupal: hook_search only for a content type

I would like to build a custom search module in Drupal 6 for searching through CCK. I need the user to search between his nodes (node.uid=x) and of a certain type (type='xyz'). I think I have to implement hook_search but I don't know where to put my filters. Can anyone help me?
You already accepted an answer (which is probably the best option for you), but there are a few other ways to accomplish this.
IIRC, the Custom Search module will work for what you want.
You can copy the stock hook_search function to a custom module and modify the query. You can do something like this:
// ...
case 'search':
// Build matching conditions
list($join1, $where1) = _db_rewrite_sql();
$arguments1 = array();
$conditions1 = 'n.status = 1';
// NEW BIT START
$allowed = array(
'content_type_1',
'content_type_2',
'content_type_3',
);
$types = array();
foreach ($allowed as $t) {
$types[] = "n.type = '%s'";
$arguments1[] = $t;
}
$conditions1 .= ' AND ('. implode(' OR ', $types) .')';
$keys = search_query_insert($keys, 'type');
// NEW BIT END
This replaces the bit that extracts the type from the actual query string.
You would have to add in the bit to restruct to a particular n.uid. I have been using this method lately, rather that Custom Search, because it simpler from the user's perspective.
HTH
You might try creating a Views with an exposed filter, it's the absolute easiest way to implementing your idea.
Also you can try use CCK Facets. But Views - of course simple.

Theme CCK fieldset

I am attempting to use the CCK theme_fieldgroup_fieldset($elements) hook to convert the fieldset to a two column layout.
I thought that this shouldn't be to hard because the individual fields are in the $elements variable so all I have to do is iterate over them and print them individually. the problem is that I have no way to tell if they have been excluded from display on the "Display Fields" tab of the content type.
Does anyone have any ideas for me? Am I trying to do this the hard way or what am I missing?
Following is the solution that I came up with. The biggest problem is that it requires a database query for every field. These isn't the greatest, but it works so what can you say?
function _brioratheme_include_cck($field) {
$query = "SELECT display_settings AS ds FROM {content_node_field_instance} WHERE field_name = '%s' LIMIT 1";
$result = db_query($query, $field);
if ($result) {
$row = db_fetch_object($result);
$display_settings = unserialize($row->ds);
return !$display_settings['full']['exclude'];
}
}

How can I modify the Book Copy module in Drupal 6 to change the name of the copy to a different name?

By default, the Book Copy module will create a new book with the same exact name as the book it is copying. This can become confusing and actually caused one of the site developers to accidentally delete the original book, which was reference in menus and such and left the site in a weird state.
Had the name of the copy been something other than the name of the original, then this problem would never have occurred. I dug through the code but just couldn't seem to figure it out. Any help will be greatly appreciated. Thanks!
You can use the hook_book_copy_alter() offered in book_copy_copy_book():
...
// The function signature is: hook_book_copy_alter(&$node, $oldbid, $newbid);
drupal_alter("book_copy", $node, $bid, $newbid);
...
So, in a custom module, you could implement the following to achieve a changed title on the new node:
function yourModule_book_copy_alter(&$node, $oldbid, $newbid) {
// Adjust the title ...
$node->title = 'Copy of ' .$node->title; // TODO: Change to the variation you want
// ... and save the node again
node_save($node);
}
It turned out that I figured it out before I found any answer on here. What I did was replace:
$node->title = t('Clone of !title', array('!title' => $node->title));
with:
if( $node->title == "Project Template" ){
$node->title = "New Project From Template";
}
else{
$node->title = t('Clone of !title', array('!title' => $node->title));
}
Inside lone_node_save() function of clone_pages.inc file associated with the node_clone module.

Removing [nid:n] in nodereference autocomplete

Using the autocomplete field for a cck nodereference always displays the node id as a cryptic bracketed extension:
Page Title [nid:23]
I understand that this ensures that selections are unique in case nodes have the same title, but obviously this is a nasty thing to expose to the user.
Has anyone had any success in removing these brackets, or adding a different unique identifier?
Ultimately, you need to change the output of nodereference_autocomplete() in nodereference.module.
To do this properly, you want a custom module to cleanly override the function.
This function is defined as a menu callback, thus,
/**
* Implementation of hook_menu_alter().
*/
function custom_module_menu_alter(&$items) {
$items['nodereference/autocomplete']['page callback'] = 'custom_module_new_nodereference_autocomplete';
}
Then, copy the nodereference_autocomplete function into your custom module, changing it's name to match your callback. Then change this one line:
$matches[$row['title'] ." [nid:$id]"] = '<div class="reference-autocomplete">'. $row['rendered'] . '</div>';
Dropping the nid reference.
$matches[$row['title']] = '<div class="reference-autocomplete">'. $row['rendered'] . '</div>';
I believe the identifier is purely cosmetic at this point, which means you could also change the text however you like. If it is not purely cosmetic, well, I haven't tested to see what will happen in the wrong conditions.
I always meant to identify how to do this. Thank you for motivating me with your question.
What Grayside has posted will work... as long as you don't have two nodes with the same title. In other words, if you want to do as Grayside has proposed, you need to be aware that the nid is not entirely unimportant. The nodereference_autocomplete_validate() function does two things. It checks to see if there is a node that matches, and if so, it passes the nid on, setting it to the $form_state array. If it can't find a node, it will set an error. If the nid is present, it will be used to get the node, which also is faster, the code is here:
preg_match('/^(?:\s*|(.*) )?\[\s*nid\s*:\s*(\d+)\s*\]$/', $value, $matches);
if (!empty($matches)) {
// Explicit [nid:n].
list(, $title, $nid) = $matches;
if (!empty($title) && ($n = node_load($nid)) && $title != $n->title) {
form_error($element[$field_key], t('%name: title mismatch. Please check your selection.', array('%name' => t($field['widget']['label']))));
}
}
This just checks to see if there is a nid and checks if that node matches with the title, if so the nid is passed on.
The 2nd option is a bit slower, but it is here errors can happen. If you follow the execution, you will see, that if will try to find a node based on title alone, and will take the first node that matches. The result of this, is that if you have two nodes with the same title, one of them will always be used. This might not be a problem for you, but the thing is, that you will never find out if this happens. Everything will work just fine and the user will think that he selected the node he wanted to. This might be the case, but he might as well have chosen the wrong node.
So in short, you can get rid of the nid in the autocomplete callback, but it has 2 drawbacks:
performance (little)
uncertainty in selecting the correct node.
So you have to think about it, before going this route. Especially, since you most likely wont be able to find the problem of the selection of the wrong nodes, should it happen. Another thing to be aware of, is that the nid showing up, also brings some valuable info to the users, a quick way to lookup the node, should they be in doubt if it is the one they want, if several nodes have similar titles.
I got Grayside's answer to work, but I had to use MENU alter, instead of the FORM alter he posted. No biggy!
function custommodule_menu_alter(&$items) {
$items['nodereference/autocomplete']['page callback'] = 'fp_tweaks_nodereference_autocomplete';
}
I've found an alternative solution is to change your widget type to select list and then use the chosen module to convert your list to an autocomplete field.
This handles nodes with the same title, and actually I think the UI is better than the one provided by the autocomplete widget.
To anyone coming across this (rather old) topic by way of a google search - for Drupal 7 please consider using entityreference module and "Entity Reference" field type if possible.
You can acheive a lot more in configuration with an "Entity Reference" field. It doesn't have this problem with the nid in square brackets.
Here is the full Drupal 7 version (References 7.x-2.1) of Grayside's answer. This goes in your custom module:
/**
* Implementation of hook_menu_alter().
*/
function custom_menu_alter(&$items) {
$items['node_reference/autocomplete/%/%/%']['page callback'] = 'custom_new_node_reference_autocomplete';
}
/**
* Implementation of Menu callback for the autocomplete results.
*/
function custom_new_node_reference_autocomplete($entity_type, $bundle, $field_name, $string = '') {
$field = field_info_field($field_name);
$instance = field_info_instance($entity_type, $field_name, $bundle);
$options = array(
'string' => $string,
'match' => $instance['widget']['settings']['autocomplete_match'],
'limit' => 10,
);
$references = node_reference_potential_references($field, $options);
$matches = array();
foreach ($references as $id => $row) {
// Markup is fine in autocompletion results (might happen when rendered
// through Views) but we want to remove hyperlinks.
$suggestion = preg_replace('/<a href="([^<]*)">([^<]*)<\/a>/', '$2', $row['rendered']);
// Add a class wrapper for a few required CSS overrides.
$matches[$row['title']] = '<div class="reference-autocomplete">' . $suggestion . '</div>'; // this is the line that was modified to remove the "[nid:XX]" disambiguator
}
drupal_json_output($matches);
}

Resources