I am trying to conditionally suppress a widget from its driver. I basically have logic that if met, should display the widget and if not, should suppress it. The widget loads properties from the current page content item, and based on some properties should display or hide. I've got all the logic working, the only part left is suppressing the actual output.
I've tried returning null from the driver, however this simply outputs an empty widget (with its wrapper). How do I completely remove the widget from view?
Finally, the zone that this widget is placed in should suppress if none of the conditional widgets display.
Is this type of flexibility possible in Orchard? Preferably from my custom module not my theme, I'm trying to separate functionality from styling.
EDIT:
Here is the context of my situation and what I am trying to accomplish. I'm sure there is a much cleaner way to do this within Orchard than how I have naively designed it the first go-around: My client's copywriters tag pages as they see fit (using the Tags module/part). I have created 2 custom content types, "Testimonials" and "Offers", both with tags themselves among other properties [these are managed by a different team of copywriters].
On most "inner pages" of the website (I'm using a layer to determine the appropriate pages), the page's sidebar gets a "Testimonial" widget and a "Offer" widget. These widgets both operate the same, although independently of each other:
They grab the tags of the current page, then pull a random [Testimonial|Offer] that has any matching tags as well. There are 4 cases that can happen given any inner page: a testimonial is displayed and the offer is hidden (testimonial tag matched, offer tag didn't), the testimonial is hidden and the offer is displayed, both the testimonial and offer displays, and finally neither displays. The first 3 use cases are working great, the 4th is what I'm having difficulty with, as the sidebar [zone] still displays even if both widgets do not (returning null from their respective drivers).
A bit of context about widgets: widgets are supposed to be pieces of contents that are visible on some or all pages of the site, and that provide information that is not directly related to the main content item on the page (if there is such a thing).
As a consequence, what you are describing should not be implemented as widgets (all that you had to do to make it work attests to that further), because they are really part of the content item. Instead, you should have implemented a part or a field. You can then simply place the shape for this part of field, using placement, by specifying a top-level zone: <place the_shape_name="/the_zone_where_you_want_it:1"/>
Unfortunately I've had to use a bit of a hack so that I could move on with the project as I'm under an aggressive deadline, but if there is a better method and/or solution I'll test as I get the chance and mark as the answer.
To get it to work, I overwrote the Widget.Wrapper.cshtml file within my theme. There, I assigned a variable to the Display(Model.Child) call, and if the result is an empty string simply return. This removes any empty widget wrapping tags. (I personally feel Orchard should behave this way by default):
var child = Display(Model.Child);
// -- NOTE: shape tracing breaks this logic!
if (string.IsNullOrWhiteSpace(child.ToString())) {
return;
}
Then simply replace the #Display(Model.Child) between the header and footer with #(child)
I then added the following method to my Layout.cshtml file.
Func<dynamic, IHtmlString> CollapsableZone = x =>
{
var display = Display(x);
string zoneName = x.ZoneName;
if (string.Equals(display.ToString(), string.Format("<div class=\"zone zone-{0}\"></div>", zoneName.HtmlClassify()), StringComparison.CurrentCultureIgnoreCase))
{
return new HtmlString(string.Empty);
}
return display;
};
Simple function that assigns the display call to a variable, checks if it is an empty zone tag, and returns. I then create variables for each zone assigned to the value of the function above. I replace all the #if (Model.ZoneName != null) with #if (!string.IsNullOrWhiteSpace(zoneVariable)), and replace the calls to #Zone(Model.ZoneName) with #(zoneVariable).
This is working for the time being. It is quite a hack and brittle solution but I've had to move on to other things.
Related
I was wondering if there was a way, to show certain parts of my footer, only when in certain categories.
E.g. a email link (mailto) only when in the Category:FAQ
I am using a custom Skin.
With help of this snippet a CSS class is added to your body tag for every category the current page belongs to. You could then display or hide certain elements with help of the corresponding class.
If you are using your own, custom skin, you can just check what categories your current wikipage belongs to, by calling
OutputPage::getCategories(). This will probably affect caching, though.
if (in_array( 'FAQ', $out->getCategories() ) {
// do something
}
edit: #Florian points out below, that you should use OutputPage methods to output stuff, rather than echoing them, so I removed that unfortunate example. And as #Florian also points out, if you want this effect to persist also for users who might have selected another skin than your custom one, you'll have to use a hook, e.g.SkinTemplateOutputPageBeforeExec.
To support our organization's component library, I would like to create an enhanced selectManyMenu. A requirement is that it resemble the type that features a dropdown menu, whose menu items have checkboxes showing whether they are selected. On top of this, I would like the the top row of the drop down, the area that is always visible to show 'bubbles' for lack of a better term, containing the label of the selected item as well as an 'X' button to remove each one, similar to a 'tag' widget.
I would like to create a JSF custom component, rather than decorating the existing <h:selectManyMenu> with some jQuery plugin that hides it and renders its own widget with a bunch of obfuscated javascript that I have no idea how it works. To be honest, none of the ones I've found fit in very well with our UI, and fundamentally I don't feel as though that is good use of JSF, to slop in some jQuery widget when JSF has such robust custom component features.
To get to my current problem, I have a custom component class set up that extends UIInput and provides the encode*() methods to render a <select> with its <option> tags same as an <h:selectManyMenu> does. I'm having a problem with the decode() method:
#Override
public void decode(FacesContext context) {
Map<String, String> requestParams = context.getExternalContext().getRequestParameterMap();
String clientId = getClientId();
Object param = requestParams.get(clientId);
LOG.debug("param: {}", param);
}
The issue is param ends up being a single string no matter how many items I select. I have verified on the front end the exact same POST request is sent when I use <h:selectManyMenu>, so I am guessing I have to do something different in decode() to all of the values from the request.
For bonus points, can anyone point me to some concise explanation of the source code for, say, even just <h:inputText>. I am able to browse the source for the JSF implementation we are using, Mojarra, but it gets extremely hard to follow since it uses separate renderers, and all sorts of factories and things to select which renderer to use, etc. I have gotten by so far by leaning on composite components, with backing components when necessary, but this one and a few others coming down the pipe I think are beyond what composite components can be used effectively for.
Also, is this the best approach to accomplish what I'm after? Would I be better off creating a custom renderer for the existing <h:selectManyMenu>? That seems like an even more elegant solution since this component basically IS a selectManyMenu just rendered a little differently and having a bit of javascript on the front end.
I gather your question boils down to:
How can I obtain a request parameter with multiple values in JSF?
The answer is: use ExternalContext#getRequestParameterValuesMap().
#Override
public void decode(FacesContext context) {
Map<String, String[]> requestParamValues = context.getExternalContext().getRequestParameterValuesMap();
String clientId = getClientId(context);
String[] params = requestParamValues.get(clientId);
// ...
}
but it gets extremely hard to follow since it uses separate renderers, and all sorts of factories and things to select which renderer to use, etc
Not sure, but perhaps you're looking for What is the relationship between component family, component type and renderer type? or How do I determine the renderer of a built-in component.
Would I be better off creating a custom renderer for the existing <h:selectManyMenu>? That seems like an even more elegant solution since this component basically IS a selectManyMenu just rendered a little differently and having a bit of javascript on the front end.
Makes indeed more sense.
I've created a custom content type and a Projection page of them, but cannot shoehorn all the various Parts and Fields into a Bootstrap Collapse.
I am encountering two problems: I have too many characters in the Layout's Property Rewrite Results; or if I add, for example, #Display(Model.ContentItem.FunOpp.FunTitle.Value) to the custom .cshtml shape, I get a 'Orchard.ContentManagement.ContentItem' does not contain a definition for 'FunOpp' error; or something similar depending upon my layout.
The custom content type definition is:
Fields:
Sponsor (Text Field)
​Funding Opportunity (Link Field)
​Funding Title (Text Field)
Closing/Due Date (Text Field)
Funding Opportunity
Number (Text Field)
Accordion Collapse Number (Text Field)
Parts:
Body
I need the Funding Title to be the Accordion-Heading; and all the remaining Fields & Parts to be the Accordion-Inner.
There is a lot of content, and Bootstrap's Collapse requires a lot of markup, so obviously I am getting the "too many characters" error if I go the Token/Rewrite Results route for the Accordion-Inner.
Yet I don't know enough MVC/Razor to determine why I'm getting the Model.ContentItem errors.
Any help would be appreciated, be it somehow combining the Tokens in the Rewrite Output to save characters; or a way to display the various #Model.ContentItem.FunOpp.xxx.Value
I don't really understand much of the plumbing but Content Item is a class, but also a dynamic object. You are currently trying to get FunOpp from the class, which doesn't have such a property, only the dynamic object content item does. So you will need to do...
#{
dynamic item = Model.ContentItem;
var funtitle = item.FunOpp.FunTitle.Value;
}
Something along those lines
Enable the Module "Shape Tracer" to see the exact structure of the Model. This is the best way to figure out what orchard is doing under the hood.
I am currently unconfident, if I can put a "if/else"-construct into my view?
How much logic do you put in your views?
My dilemma:
I am rendering a navigation. So, I have to differ between the current/active menu item and the rest. The current menu item gets a special css class. I don't know how to handle this in a better way than using if-else.
If you are doing MVC (hopefully you do), than the question is "Do I put the logic in the view or the controller?". I use a simple rule to find out the answer of that:
What if my view was not HTML, but an XML document?
If I will need this logic in both circumstances - its place is in the controller. If not - it's in the view.
In good MVC design you should be able to swap the views without touching the controller.
As much as is necessary to display the information. Just remember that the view is just a window into the internal state of the program. If you stripped the view layer completely away, the program should still be able to operate as usual, just without being able to see what it's doing.
edit: re your navigation, that seems like an okay use of an if statement. The information about which is active is still coming from the model, you're simply using the if statement to decide how to display it. You might consider a little bit about how you're rendering your navigation: is the information about which navigation items available, and which to render living in your view or your model?
One way you might choose to approach the situation is to have the model give you a list of navigation items, along with which one is active, and program the view to know how to generate appropriate HTML from that. That code might contain precisely one if statement total. (instead of one for each nav item).
I wouldn't worry about putting an if statement in a view. In fact, I think there's a bit too much hand-wringing (in general) about responsibilities in these kinds of situations.
If you make your views too dumb then your model can become too view-sentric (tightly coupled).
IMHO a view can do what it likes but the guiding principle should be: where does it get its information from? If the answer is "the model" then use as much logic as you like.
An "if/else" construct is fine if the view is alternating modes, e.g. formatting a U.S. address vs. a foreign address in an order screen.
However, any logic that you place into a view should not alter the model.
Add this helper to your application_helpers.rb It will surround your links with <li> and <li class="active"> if the link is the current page.
Use it in place of a link_to.
link_to 'home', root_url, optional_condition_argument_goes_here
def active_link_to(text, url, condition = nil)
if condition.nil? and String === url
condition = url == request.path
end
content_tag :li, link_to(text, url), :class => (condition && 'active')
end
(Courtesy of Mislav)
I might put if-else in a view. In most cases not. The real question in my mind is whether the logic could go anywhere else without being messier.
I tend to avoid putting control-flow logic in my views (ASP.NET MVC) except under circumstances where I may want a portion of the interface visible/not visible based on the presence or absence of data. In this case, it is view logic -- I'm determining the layout of the page, the elements of the page that are available, etc. I think that this is perfectly acceptable and preferable to having the controller determine this or multiplying views to account for minor variants. The controller needs to give the view enough information for it to be able to render the view and its variants as needed.
What I wouldn't put into the view is business logic that determines how to calculate something or whether to perform some action (except, perhaps, for role-based decisions -- these seem to crop up just about everywhere). Other than client-side validation, my business logic resides in the controller/model.
An example of where I might use if/then logic in a view is a view which displays events. In my app, events can have subevents, but subevents can't have further subevents: a two level hierarchy. On my display page, I have tabs for Details, Groups, Participants, and Subevents. These are the same for both events and subevents, with the exception of the Subevent tab. It shouldn't exist for a subevent. Rather than repeat myself by having two different views that are virtually identical except for that one tab, I've added a tiny amount of logic to the view to not render the Subevent tab if an event has none.
By the same token, I wouldn't go so far as to have a single "view" that uses logic to determine whether to show an overview or details or editing pane, etc. based on the value of some view data item. This seems an abuse of the single responsibility principle as applied to views. Each view should have a single-purpose, IMO.
Logic that is in the view should not be required to fully describe the current state of the model.
Said another way, logic in the view is acceptable if that logic is used to format or alter the visualization of the information. Logic in the view might also have a use in vetting data that is entered before taking the expense of transmitting that data to the controller (in a client/server or web application).
For instance, the view might include logic to split a list of items into multiple columns when the list is longer than N items. That split could be done in several different ways according to the exact nature of the view (e.g. http, mobile device, pdf, voice reader, Morris Code, etc, etc, ad nasium). The full view information might need to be paginated - and that can only be done in the view. Formatting logic should not ever be included in the controller or the model.
As a corner case, the view might include logic to check that a password entered for a new user meets the current security requirements (e.g. double entry of password matches; at least N characters long; does not include spaces or the "*" character; includes at least three of the following: lower case letters, upper case letters, numbers, symbols; is different than the last N passwords, no based on a dictionary word, etc, etc). Depending on the nature of the logic, vetting a password could be thought of as "formatting" or as "business logic". It might be that the check happens in two passes - one set of checks for formatting in the view, and another set of checks in the controller with info from the model (the last N passwords).
I am displaying 3 or more versions of a form. One version is an edit form to edit all fields. A second version will be a read only version of the same form which will be used to show all the same fields but with all fields having readonly="true" on the client side so that the user cannot enter data. The readonly fields need to use a different css style. This is to display archived data. I am already hiding the submit button so they can't submit but I want the form to look like it is readonly. A third version will have some fields readonly and some editable for a particular class of users that has limited editing privileges.
I am using ASP.NET MVC 1.0. How do I modify all (or a subset) of the fields displayed so they are readonly. I would like to iterate through the collection of fields in the controller and set them all to readonly and also set the correct css class. I don't want to have to put an if statement on every field in the .aspx file (there are 40-50 fields) and I'd prefer not to have this on client side so I can prevent users from modifying javascript/html to edit things they are not supposed to.
TIA,
Steve Shier
Keep in mind that even if you set the tags as readonly on the server side, users can still change them through a variety of means, and whatever the value on the form is before it gets sent back to you.
Certainly the easiest way is client-side with jQuery:
$(function() {
$('input, select, textarea').attr('disabled', 'disabled');
});
Or, you could do it in your View, but it's ugly. Off the top of my head, you would need some sort of bool passed into the View (via ViewData I suppose), and check that on each Input to see if you should add the disabled attribute. Not my idea of fun...
I would have different views that correspond to your states and then choose the view depending on which state you are in. You could also implement it with partials, breaking down the pieces so that you can easily include editable or read-only versions of the different sets of elements. The read-only view, then, need not even include a form element. You could also present the data in spans, divs, or paragraphs rather than as input elements.
Note: you'll still have to check whether the current user has the ability to update/create data in the actions that process form submits. Just because you limit the ability to view data in a read-only format, that won't stop someone from crafting a form post to mimic your application if they want. You can't rely on hiding/disabling things on the client to prevent a malicious user from trying to enter/modify data.
I usually use partial views to represent forms and/or parts of forms.
I can think of two simple ways to do what you need (as I understood it):
<% Html.RenderPartial(the_right_partial, model); %> where the_right_partial is either a value passed from the controller or a helper (in which case, the_right_partial(something));
pass a bool or enum paramether from controller representing editability and then using a helper to obtain the right htmlAttributes, like:
<%= Html.TextBox("name", value, Html.TheRightHtmlAttributesFor(isReadableOrNot)) %>;
There may be other ways, like creating new helpers for input fields which accept an additional isReadableOrNot arg (but it seems an overkill to me), or like mangling the html/aspx in some odd (and totally unreadable/unmaintainable way), but I'd not suggest them.
Notice that using html attributes like disabled is client side, and with tools like firebug it takes just two seconds to change them.
Others have already said it, but I also have to: always assume that the user will do his/her best effort to do the worst possible thing, so check the user rights to modify stuff on server side, and consider client side checks as a courtesy to the user (to let her/him understand that the form is not supposed to be edited, in this case).
Since I am trying to use a single partial for the different states of the form, I am thinking I will create helper functions which will display correctly based on the state and the user. The helpers will use a dictionary of fields that will indicate under which condition the field is read only. I will still have server side checks to make sure data is valid and the user is authorized to make changes.
Thanks for all of your ideas and help.
Steve