Add a field to the Umbraco 8 Members Index - umbraco

I have extended the Umbraco 8 Member type by adding some custom fields.
I need to be able to search Members by one of these fields, 'organisationName' (that's it's alias), so when looking at Examine, I tried to add this to the members index like this:
private readonly IExamineManager _examineManager;
public CustomizeIndexComponent(IExamineManager examineManager)
{
_examineManager = examineManager;
}
public void Initialize()
{
// get the external index
if (!_examineManager.TryGetIndex("MembersIndex", out var index))
return;
// add a custom field type
index.FieldDefinitionCollection.TryAdd(new FieldDefinition("organisationName", FieldDefinitionTypes.FullText));
}
when I put a breakpoint on after the TryAdd I can see the new field, but in the back office it's not there when I look at the members index.
Am I going about this the right way, as in can I actually add my field to the members index, or should I create a new custom index based on the member?

I think most people create their own index like here:
https://our.umbraco.com/forum/developers/extending-umbraco/72299-umbraco-7-backoffice-member-search-by-custom-properties#comment-277928
But I would personally just access the membership API with GetMembersByPropertyValue.
It's very easy to make a call to the member api with umbraco api controller.
https://our.umbraco.com/documentation/reference/management/services/memberservice/
(here an example just to show the few lines).
/umbraco/api/SearchMemberApi/ReturnMembersWith

Related

How to load navigation property in controller without having them in View

I have two classes say Author and Book.
I show you simplified code here
class Author
{
int Id
string Name
virtual ICollection<Book> Books;
}
class Book
{
int Id
int AuthorId
string Name
bool Flag
}
In one of my views, I want to edit Author but when saving the object, I want to update some of his books and set the Flag to true;
My save method looks like this
public ActionResult SaveAuthor(Author data)
{
// call Web API to save Author and also update Flag for some of the data.Books.
// but data.Books is empty at this point.
}
In this method, the data parameter, includes author information but, it does not include the Books information so data.Books is empty. The reason is I don't have Books information in my View and View does not bind the Books information.
My question is how I can populate the Books collection inside SaveAuthor method or preferably inside Web API which saves the author.
One answer is I should include all books (as hidden field) for the author in view so I can get it back in data parameter.
Is there any other way achieve this goal without adding Books into View?
I'm using VS 2015 and probably MVC4

Grails binding one to one associations

When you generate grails views, grails looks at your relationships and generates the right html for your form data to be automatically binded to the back end domain. For one to one associations grails creates a drop down list.
However, you might not want to present that property as a drop down list but something more custom (for example a text field with autocomplete). As soon as you do that the value that comes to the controller from that field, comes in as a String and you have to first:
Clear errors
Perform a findBy based on a given param and assign it to the property of the domain
I really want to avoid doing findBys in the controller as much as possible because it seems like I am doing logic/things that should not go there. The controller should delegate to the Service layer. It is not clear to me from the grails documentation how would I do that by using bindData which seems to work really well with String, date, Integer properties etc.. but I do not see how bindData is used for properties that are other domains.
I also really want to avoid passing the params object to the Service layer as it seems less reusable (or maybe not, correct me if I am wrong). I guess that I do not like how it looks semantically. I would prefer the first over the second:
#Transactional
class WithdrawService {
def addWithdraw(Withdraw withdraw) {
//perform business logic here
}
def createWithdraw(Map params){
//perform business logic here
}
}
Let's take the following example:
class Withdraw {
Person person
Date withdrawDate
}
and the parent lookup table
class Person {
String name
String lastName
static constraints = {
}
#Override
public String toString() {
return "$name $lastName"
}
}
In order for the bind to happen automatically without any extra work grails passes in the following request params to automatically bind the one to one:
person.id
a person map with the id.
[person.id:2, person:[id:2], withdrawDate:date.struct, withdrawDate_month:11, create:Create, withdrawDate_year:2015, withdrawDate_day:10, action:save, format:null, controller:withdraw]
What is the best way to go about this?
Pass two hidden fields that look exactly like this: person.id:2, person:[id:2] that get populated as a result of the Ajax call that populates the autocomplete?
In the controller do a Person.findBySomeKnownProperty(params.someKnownValue)
Or any other approach?

How to print a list of objects in a Velocity template?

This is a pretty basic problem and I'm pretty sure I'm doing something wrong or making some assumption. Here goes.
I'm writing a Jira plugin, which uses the Velocity template system. I have a list of ResultRow objects where ResultRow is a class with a single member variable: String key:
public class ResultRow {
public String key;
}
I have a list of these ResultRows:
List<ResultRow> rows = new ArrayList<ResultRow>();
ResultRow row = new ResultRow();
row.key = "foo";
rows.add(foo);
Map<String, Object> velocityParams = new HashMap<String, Object>();
velocityParams.put("rows", rows);
return descriptor.getHtml("view", velocityParams);
and I am trying to list these rows in a template with the following:
#foreach ($row in $rows)
<tr><td>$row.key</td></tr>
#end
I want the output to be: foo. Maddeningly, the template system simply prints the literal string "$row.key" instead of the contents of key. To verify that "$row" is indeed an object, I used the template:
#foreach ($row in $rows)
<tr><td>$row</td></tr>
#end
and the result was as expected: com.domain.jira.ResultRow#7933f2c6.
I think maybe I'm missing some requirement for the class. Does it need to be defined in some special way to suggest to Velocity that certain members are usable in templates? Does Jira use some special funky version of Velocity that only works with certain objects?
I guess the answer is you cannot do what I was trying to do. You can call member methods but you can't access member variables, which means you'll need to add getters to your class. (Could've sworn I tried that. Ah well.)
Velocity does not expose fields, only methods. There are ways to change that:
You can create your own Uberspect class that allows access to public fields.
You can wrap the instance with a modified version of Velocity's FieldMethodizer that gives access to non-static fields.
You can add and use an instance of a "tool" class to your context, such as a subclass of VelocityTool's ClassTool.

Prevent user from editing related records

I have a Note domain object which belongs to a Document object. Only an owner of a Document can add Notes so in the Document class there is a canUserAccess() method. In my service layer I can call canUserAccess() to ensure a user only adds Notes to Documents they own.
This works well for create but I have hit a problem with my Note edit action. On post, the viewmodel is mapped to a Note object, providing it with a DocumentID. Problem is, a malicious user could send in a DocumentID on which they do have permission and thus edit a Note belonging to a Document they don't. In my service layer I cannot reliably use the supplied DocumentID yet I need to get the related DocumentID in order to verify that the user can edit the Note. This is an example:
public void editNote(Note note)
{
note.Document = documentRepository.Find(note.DocumentID);
if(note.Document.canUserAccess())
}
How do I get around this? It seems I need to avoid passing the DocumentID with the edit viewmodel but how do I hydrate the related Document in the service layer? There is probably a really simple solution to this and I am just tying myself up in circles!
You do this with BindAtrribute for the model or for the action method by adding a white list with the properties you want to be bound :
for the model
[Bind(Include="NoteText,NoteTitle")]
public Model{}
for the action method
public ViewResult Edit([Bind(Include="NoteText,NoteTitle"]Note note)){}
or use a black list for the properties you don't want to bind :
[Bind(Exclude="DocumentID")]
public Model{}
I would personally use white list with the model class. You might find this article interesting. The last section for under-posting is your case.
Then you don't have the documentID passed, but in your action you can do this:
public ViewResult Edit(Note note)
{
Note updateNote = nodesRep.Get(note.noteID);
if(updateNote.Document.canUserAccess())
{
//replace the values of the updateNote
//you can place this line in your base Repository class
context.Entry<Note>(updateNote).CurrentValues.SetValues(note); //I assume EF 4.1
}
}

Using LLBL as Model in MVC

I have settled on trying to use ASP.NET MVC but the first part I want to replace is the Model. I am using LLBL Pro for the model.
I have a table called "Groups" that is a simple look up table. I want to take thhe results of the table and populate a list in MVC. Something that should be very simple... or so I thought.... I've tried all kinds of things as I was getting errors like:
The model item passed into the dictionary is of type 'System.Collections.Generic.List1[glossary.EntityClasses.GroupEntity]', but this dictionary requires a model item of type 'System.Collections.Generic.IEnumerable1[glossary.CollectionClasses.GroupCollection]'.
private GroupCollection gc = new GroupCollection();
public ActionResult Index()
{
gc.GetMulti(null);
return View( gc.?????? );
}
This is all I am trying to do, I've tried lots of variations, but my goal is simply to take the data and display it.
Not sure if this would work, but you could try wrapping the EntityCollection into a ViewModel class and passing it to the View like so:
public class GroupsViewModel()
{
public GroupCollection Groups { get; set; }
// other items in your view model could go here
}
then convert your controller method to
public ActionResult Index()
{
GroupCollection gc = new GroupCollection();
gc.GetMulti(null);
GroupsViewModel vm = new GroupsViewModel();
vm.Groups = gc;
return View(vm);
}
I like this approach because each ViewModel is an object in-and-of itself.
You can use the AsEnumerable extension where your ????? are or change the type of your ViewUserControl(in the markup) to be of type System.Collections.Generic.List. Basically what you need to correct is the mismatch between the type of the View and the Model being passed in.
I'm not sure about your exact error, but I'd venture a guess that one of two things are happenging:
You are making some sort of invalid / illegal call on your LLBLGen object. If this is the case make sure you are setting it up right / calling right method / property etc.
The model you are passing to the veiw is too hairy for it to deal with. In this case, and in general, you should create a light 'View Model' class with just the data you want displayed and populate it from your LLBLGen object first then pass it to the view, which will be able to easily handle your view model class.
Here are some references:
http://stephenwalther.com/blog/archive/2009/04/13/asp.net-mvc-tip-50-ndash-create-view-models.aspx
http://nerddinnerbook.s3.amazonaws.com/Part6.htm
http://www.codinginstinct.com/2008/10/view-model-inheritance.html
Stemming off what Yuriy said, it looks like your view is strongly typed to a "collection" of a collection of your groupentity, and you are trying to pass just the collection of your groupentities. Make sure your "collection" type (IEnumerable, IList, etc) matches what type of collection you are sending in your controller, along with the type of the actual object in the collection.
View:
System.Collections.Generic.List1[glossary.EntityClasses.GroupEntity]
Controller:
System.Collections.Generic.List1[glossary.EntityClasses.GroupEntity]
Just a thought

Resources