I understand why this is happening but I need a work-around. I looked into some other questions on StackOverflow but none of them was helpful. I do not want disable input validation throughout the whole website because that is definitely dangerous. I have only one (at least for now) place where I need to disable input validation.
I decorated the Action Method with [ValidateInput(false)] attribute, and I'm encoding the strings with Html.Encode. But still, I get the same error. Here's my view:
<div id="sharwe-categories">
<ul class="menu menu-vertical menu-accordion">
#foreach(var topLevel in Model)
{
var topLevelName = Html.Encode(topLevel.Name);
<li class="topLevel">
<h3>
#Html.ActionLink(topLevel.Name, "Index", "Item", new { category = topLevelName }, new {#class = "main"} )
</h3>
<ul>
#foreach (var childCategory in topLevel.Children)
{
var childcategoryName = Html.Encode(childCategory.Name);
<li>#Html.ActionLink(childCategory.Name, "Index", "Item", new RouteValueDictionary { { "category", topLevelName }, { "subcategory", childcategoryName } }, null)</li>
}
</ul>
</li>
}
</ul>
</div>
As you can see, there's no user input. But some of the category names have some "dangerous" characters in them... Any solutions?
Although Darin's answer is perfectly feasible, I wouldn't recommend using Scott Hanselman's technique of turning all this validation off step by step. You will sooner or later end up in deep...
Second suggestion of using IDs along with dummy strings (which are great for SEO and people) is a way to go, but sometimes they're not feasible either. Imagine this request URL:
/111/Electronics/222/Computers/333/Apple
Although we'd have these IDs we can rely on and human/SEO friendly category names as well, this is definitely not desired. ID + Dummy string is feasible when we need to represent one single item. In other cases it's not. And since you have to display category and subcategory, this is a problem.
So what can you do?
Two possible solutions:
Cleanup category names to only have valid characters - this can be done but if these are not static and editable by privileged users, you're out of luck here, because even if you've cleaned them up now, someone will enter something invalid later
Cleanup your string on the go - When you use category name, clean it up and when reading it and using it (to get the actual category ID) you can compare provided (previously cleaned) category name with value in DB that you clean up on the fly either:
now while filtering categories
before when generating category names
I'd suggest you take the 2.2 approach. Extend your DB table to have two columns:
Category display name
Category URL friendly name
you can also set a unique constraint on the second column, so it won't happen that two of your categories (even though they'd have different display names) would have same URL friendly names.
How to clean
The first thing that comes to mind is to strip out invalid characters, but that's very tedious and you'll most probably leave something out. It's much easier and wiser to get valid characters from your category display name. I've done the same when generating dummy URL category names. Just take out what is valid and bin the rest. It usually works just fine. Two examples of such regular expressions:
(\w{2,}) - only use letters, digits and underscores and at least two of them (so we leave out a or single numbers and similar that doesn't add any meaning and unnecessarily lengthens our URL
([a-zA-Z0-9]{2,}) - only letters and digits (also 2+)
Get all matches in your category display name and join them with a space/dash and save along with original display name. A different question of mine was exactly about this.
Why an additional column? Because you can't run regular expression in SQL server. If you're using MySql you can use one column and use regular expression on DB as well.
Even though you shouldn't do it this way...sometimes there is not an easy way to get around it. requestPathInvalidCharacters on the httpRuntime tag in web.Config is what you seek. Just enter the following into the <system.web> section:
<httpRuntime requestPathInvalidCharacters="<,>,*,%,:,\" />
I would highly encourage locking this down using the following instead:
<location path="the/path/you/need/to/lock/down">
<system.web>
<httpRuntime requestPathInvalidCharacters="<,>,*,%,:,\"/>
</system.web>
</location>
Just toss that into the root <configuration> tag. That way...you're not opening your entire site to allow for ampersand's in the path...and potentially exposing the entire site to an unforseen attack.
You may find the following blog post useful about using special characters in urls. But in general it is best practice to replace those titles with slugs the same as StackOverflow does with question titles in the url for better SEO and use ids to identify them.
Related
I wrote and support a little web app for our local animal shelter to help volunteers locate dogs. I chose Tabulator because it had great features and was easy to use and have been very happy with my choice. For the first version of the app I used external input fields to search and manually did all the wiring to support live search.
Now I am working on v2 and am trying to use header filters. My problem is that the filters need to be exclusive, that is, using filter1 clears/disables filters 2 and 3, using filter2 clears/disables 1 and 3, and so on. With the external search fields I used focus() events to do this. When I try using jQuery on(focus) delegates to do the same with header filters and for example table.setHeaderFilterValue("field1", "") it does not work; the event triggers but the input box never gets the focus so I cannot type in it. I've tried different events like click; but nothing I've tried works properly.
I've studied the docs and struggled with this for several hours. I've considered hooking dataFiltering() and eliminating the filters I don't want, but I'm not sure how to identify the filter that I want to keep, and there is still the matter of the text in the fields to be dealt with. I'm sure it doesn't help that front-end work is not my area of expertise, though so far I've managed well enough. Is there a simple or normal way to do this that I'm just not seeing?
For the record, I found a way to do exclusive filtering with header filters with a single event callback:
\$(document).on("focus", ".tabulator-col input[type=search]", function() {
var hfNames = ["name", "anum", "kennel"];
var fieldName = \$(this).closest(".tabulator-col")[0].getAttribute("tabulator-field");
hfNames.map(function(hfN) { if (hfN != fieldName) table.setHeaderFilterValue(hfN, "") });
});
The three hfNames are the field names of the columns with filtering on. Yes, I could have derived them dynamically, but it didn't seem worth it for a small app like this.
As I suspected, the key was simply a better knowledge of JQuery.
I'm trying to create a product filter with deep-linking capability. Essentially, I want the user to be able to filter my product list on multiple categories and have the URL reflect the filtering they've done.
So it would start as:
www.site.com/products/
My first level of category filtering already works. So I can use EE's regular handling of URL segments to get to my first level of filtering. For instance:
www.site.com/products/leatherthongs
Returns a filtered subset showing only a spectacular collection of leather thongs. But now I want the user to be able to filter on another category - color for instance. This is where stuff stops working.
EE's way of handling multiple categories inside templates (with ampersands or pipes) doesn't work in the URL:
www.site.com/products/leatherthongs&red
Nor does any variation that I've tried.
My next move is to create a simple raw PHP method that can capture regular querystring parameters and then inject them into the {entries} tag before rendering. Not very difficult, but quite ugly. I would love to know if there is a way to handle multiple categories in the URL natively.
Thanks for your time.
Have you considered using Low's Seg2Cat add-on? I'm not sure how complex you want to make this but it seems that you could specify something in your channel:entries loop like categories='{segment_2){if segment_3}|{segment_3_category_id}{/if}'
This exact syntax is untested but I have had success in the past with a similar solution.
Is this the most efficient way to filter a load of channel entries? I want to display entries that have no comments and that are not sticky. I'm using this code.
{exp:channel:entries channel="{segment_3}" status="open" orderby="date" disable="categories|category_fields|member_data|pagination"}
{if comment_total == "0" AND sticky == 'n'}
...
{/if}
{/exp:channel:entries}
Cheers
Lee
Using conditional variables, probably. But this will likely return lots more results than you need. Plus you won't be able to accurately use {count} (maybe not an issue for you, though).
Another approach which doesn't use conditional variables, but just goes straight after the results you want, and only the results you want, is to use the Query Module
{exp:query sql=
"SELECT title
FROM exp_channels
JOIN exp_channel_titles ON exp_channels.channel_id = exp_channel_titles.channel_id
WHERE exp_channels.channel_name = '{segment_3}'
AND exp_channel_titles.status = 'open'
AND exp_channel_titles.sticky = 'n'
AND exp_channel_titles.comment_total = 0"
}
<li>{title}</li>
{/exp:query}
This could get tedious if you needed to access a bunch of custom fields, but it is an efficient way to get the results you want.
Sticky is an available parameter on the entries loop, so you could filter at least that element in the entries loop itself by simply adding sticky="no", but comments, unfortunately, is not an available parameter, so Alex's suggestion may be the best option if your requirements are fairly simple. If you need access to a significant number of custom fields, however, it may be a bit tricky. So you'll have to decide what approach to take based on what you need within your loop.
I'm working on a very, very quick and dirty application using almost entirely scaffold to do it. This is only for internal use, and it's just to replace a spreadsheet, so while I know that I shouldn't rely on scaffolds for real production use, I still intend to use them where I can for efficiency (...just wanted to make sure we aren't taken off track by the inevitable question!).
What I am curious about is since scaffolds can do quick and dirty CRUD, and you can do dynamic finders for searches, is there a way to dynamically show the "list" action of the scaffold with a constraint (i.e. show all items in the list that were created by this "user")? That would be fine for me...
Alternatively, is there anything available in Grails to allow "grouped" tables. Essentially the "list" action from the scaffold, but grouped by a particular attribute (again, think of grouping all the items by the user that created them).
Any help would be appreciated - I've scoured Google and some books with no luck, but wanted to check with the StackOverflow community before I dismiss the notion and write it by hand. Thanks!
We did the same in our Grails application and it's relatively easy:
Create a new form in the list.gsp referring to the list action <g:form action="list" method="post">
Inside that form, declare a table with the fields you want to search on:
<div class="buttons">
<span class="button"><g:submitButton name="list" class="search" value=" ${message(code: 'default.button.search.label', default: 'Search')}"/></span>
</div>
In the list action, build up the query (using findAll, findWhere, .. whatever takes your fancy) and return that to the page:
[wardInstanceList : Ward.findAll(whereClause, [max: params.max, offset: params.offset]) , wardInstanceTotal : (int) Ward.executeQuery("select count(*) " + whereClause).get(0)]
The whereClause looks something like: "from Ward where ward = 'something' and room = 'something else'"
In the context of Domain Driven Design, is a StackOverflow tag (ie. ddd ) a value object or entity?
EDIT:
Imagine, that you have to build SO website. How would you consider 'tag'?
To expand a little on awhite's answer
a tag is a value type
Why?
Because it doesn't make sense to have
var tag1 = new Tag("DDD");
var tag2 = new Tag("DDD");
Assert.AreNotEqual(tag1, tag2);
clearly they should be equal to each other because a tag has no identity except for its label. Questions and answers on the other hand are definitely entities
SO tag is most likely an entity. Tags can be created, merged, deleted and renamed. There are features like 'similar tags', user's tags etc. Some of these functions, especially life cycle, will require an identity. Classic DDD example where Person that changes his/her name is still the same person, the same identity. The same with tags where user can decide to rename "domain-driven-design" to "DDD" and it will still be the same thing. Tags also need additional attributes like tag.Id, tag.Name, tag.CreatedOn, tag.CreatedBy, tag.Locked etc. There would probably be a corresponding tags repository that can enforce name uniqueness rule.
To summarize, SO Tag is not a DDD Value Object because it is mutable and has a life cycle. More importantly, Tag is not only a characteristic of a Question (this is what I think was overlooked by other answers). It participates in a lot more relationships than that. In other words, Tag is more than just a sum of its attributes, it also has 'conceptual identity'. On the other hand TagName is a perfect example of Value Object. Its only purpose in life is to describe another entity (Tag). TagName is nothing more than just a string that may have a few built in rules like max length and case insensitive comparison. It may also make sense to simply use String instead.
Code that displays questions may use something like this:
IList<TagName> tags = question.GetTags();
Code that tags the question can look like this:
void TagQuestion(Question q, TagName tagName) {
Tag tag = _tagsRepository.FindByName(tagName);
if (tag == null) {
tag = CreateNewTag( /* capture creator, date, other rules*/);
}
q.AddTag(tag);
}
Just some additional considerations: Tags can be normalized, "DDD" should be equal to "ddd" and "DdD", and in most tag systems, spaces get replaced with "_" underscores. Also I guess the creator will be tracked for the badge system.