How to make a plone view that inserts other smaller views of content items? - templates

I think this should be simple. I have a folderish TTW dexterity content item (a drop box) that contains folderish TTW dexterity items (proposals). Each proposal contains TTW dexterity reviews that have fields I want to summarize.
I can easily make a view that generates a table as indicated below for any proposal with simple modifications to the folderlisting view:
[review1 link] [criterion_1 value] [criterion-2 value]...
[review2 link] [criterion_1 value] [criterion-2 value]...
.
.
I can also generate a working table view for a drop box by modifying the folderlisting view:
[proposal1 link] [column I would like to insert the above table in for this proposal]
[proposal2 link] [column I would like to insert the above table in for this proposal]
.
.
My problem is I cannot figure out how to insert the first table into the cells in the second column of the second table. I've tried two things:
Within the view template for the dropbox listing, I tried duplicating the repeat macro of the listingmacro, giving it and all its variables new names to have it iterate on each proposal. This easily accesses all of the Dublin core schemata for each review, but I cannot get access to the dexterity fields. Everything I have tried (things that work when generating the first table) yield LocationError and AttributeError warnings. Somehow when I go down one level I lose some of the information necessary for the view template to find everything. Any suggestions?
I've also tried accessing the listing macro for the proposal, with calls like <metal use-macro="item/first_table_template_name/listing"/>. Is this even partially the right approach? It gives no errors, but also does not insert anything into my page.
Thanks.

This solution is loosely based on the examples provided by kuel: https://github.com/plone/Products.CMFPlone/blob/854be6e30d1905a7bb0f20c66fbc1ba1f628eb1b/Products/CMFPlone/skins/plone_content/folder_full_view.pt and https://github.com/plone/Products.CMFPlone/blob/b94584e2b1231c44aa34dc2beb1ed9b0c9b9e5da/Products/CMFPlone/skins/plone_content/folder_full_view_item.pt. --Thank you.
The way I found easiest to create and debug this was:
Create a minimalist template from the plone standard template folder_listing.pt which makes just the table of summarized review data for a single proposal. The template is just for a table, no header info or any other slots. This is a stripped version, but there is nothing above the first statement. A key statement that allowed access to the fields were of the form:
python: item.getObject().restrictedTraverse('criterion_1')
The table template:
<table class="review_summary listing">
<tbody><tr class="column_labels"><th>Review</th><th>Scholarly Merit</th><th>Benefits to Student</th><th>Clarity</th><th>Sum</th></tr>
<metal:listingmacro define-macro="listing">
<tal:foldercontents define="contentFilter contentFilter|request/contentFilter|nothing;
contentFilter python:contentFilter and dict(contentFilter) or {};
I kept all the standard definitions from the original template.
I have just removed them for brevity.
plone_view context/##plone;">
The following tal:sum is where I did some math on my data. If you are
not manipulating the data this would not be needed. Note that I am only
looking at the first character of the choice field.
<tal:sum define="c1_list python:[int(temp.getObject().restrictedTraverse('criterion_1')[0])
for temp in batch if temp.portal_type=='ug_small_grants_review'];
c1_length python: test(len(c1_list)<1,-1,len(c1_list));
c2_list python:[int(temp.getObject().restrictedTraverse('criterion_2')[0])
for temp in batch if temp.portal_type=='ug_small_grants_review'];
c2_length python: test(len(c2_list)<1,-1,len(c2_list));
c1_avg python: round(float(sum(c1_list))/c1_length,2);
c2_avg python: round(float(sum(c2_list))/c2_length,2);
avg_sum python: c1_avg+c2_avg;
">
<tal:listing condition="batch">
<dl metal:define-slot="entries">
<tal:entry tal:repeat="item batch" metal:define-macro="entries">
<tal:block tal:define="item_url item/getURL|item/absolute_url;
item_id item/getId|item/id;
Again, this is the standard define from the folder_listing.pt
but I've left out most of it to save space here.
item_samedate python: (item_end - item_start < 1) if item_type == 'Event' else False;">
<metal:block define-slot="entry"
The following condition is key if you can have things
other than reviews within a proposal. Make sure the
item_type is proper for your review/item.
tal:condition="python: item_type=='ug_small_grants_review'">
<tr class="review_entry"><td class="entry_info">
<dt metal:define-macro="listitem"
tal:attributes="class python:test(item_type == 'Event', 'vevent', '')">
I kept all the standard stuff from folder_listing.pt here.
</dt>
<dd tal:condition="item_description">
</dd>
</td>
The following tal:comp block is used to calculate values
across the rows because we do not know the index of the
item the way the batch is iterated.
<tal:comp define = "crit_1 python: item.getObject().restrictedTraverse('criterion_1')[0];
crit_2 python: item.getObject().restrictedTraverse('criterion_2')[0];
">
<td tal:content="structure crit_1"># here</td>
<td tal:content="structure crit_2"># here</td>
<td tal:content="structure python: int(crit_1)+int(crit_2)"># here</td>
</tal:comp>
</tr>
</metal:block>
</tal:block>
</tal:entry>
</dl>
<tr>
<th>Average</th>
<td tal:content="structure c1_avg"># here</td>
<td tal:content="structure c2_avg"># here</td>
<td tal:content="structure avg_sum"># here</td>
</tr>
</tal:listing>
</tal:sum>
<metal:empty metal:define-slot="no_items_in_listing">
<p class="discreet"
tal:condition="not: folderContents"
i18n:translate="description_no_items_in_folder">
There are currently no items in this folder.
</p>
</metal:empty>
</tal:foldercontents>
</metal:listingmacro>
</tbody></table>
Create another listing template that calls this one to fill the appropriate table cell. Again, I used a modification of the folder_listing.pt. Basically within the repeat block I put the following statement in the second column of the table:
This belongs right after the </dd> tag ending the normal item listing.
</td> <td class="review_summary">
<div tal:replace="structure python:item.getObject().ug_small_grant_review_summary_table()" />
</td>
Note that "ug_small_grant_review_summary_table" is the name I gave to the template shown in more detail above.

Related

RegEx Parsing for HTML attributes - one specific string

With Delphi Rio, I am using an HTML/DOM parser. I am traversing the various nodes, and the parser is returning attributes/tags. Normally these are not a problem, but for some attributes/tag, the string returned includes multiple attributes. I need to parse this string into some type of container, such as a stringlist. The attribute string the parser returns already has the '<' and '> removed.
Some examples of attribute strings are:
data-partnumber="BB3312" class=""
class="cb10"
account_number = "11432" model = "pay_plan"
My end result that I want is a StringList, with one or more name=value pairs.
I have not used RegEx to any real degree, but I think that I want to use RegEx. Would this be a valid approach? For a RegEx pattern, I think the pattern I want is
\w\s?=\s?\"[^"]+"
To identify multiple matches within a string, I would use TRegex.Matches. Am I overlooking something here that will cause me issues later on?
*** ADDITIONAL INFO ***
Several people have suggested to use a decent parser. I am currently using the openSource HTML/DOM parser found here: https://github.com/sandbil/HTML-Parser
In light of that, I am posting more info... here is an HTML Snippet I am parsing. Look at the line I have added *** at the end. My parser is returning this as
Node.AttributeText= 'data-partnumber="B92024" data-model="pay_as_you_go" class="" '
Would a different HTML DOM parser return this as 3 different elements/attributes? If so, can someone recommend a parser?
<section class="cc02 cc02v0" data-trackas="cc02" data-ocomid="cc02">
<div class="cc02w1">
<div class="otable otable-scrolling">
<div class="otable-w1">
<table class="otable-w2">
<thead>
<tr>
<th>Product</th>
<th>Unit Price</th>
<th>Metric</th>
</tr>
</thead>
<tbody>
<tr>
<td class="cb152title"><div>MySQL Database for HeatWave-Standard-E3</div></td>
<td><div data-partnumber="B92024" data-model="pay_as_you_go" class="">$0.3536<span></span></div></td> *****
<td><div>Node per hour</div></td>
</tr>
<tr data-partnumber="B92426">
<td class="cb152title">MySQL Database—Storage</td>
<td><span data-model="pay_as_you_go" class="">$0.04<span></span></span></td>
<td>Gigabyte storage capacity per month</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</section>
The documentation for the parser you are using says TDomTreeNode has an AttributesText property that is a "string with all attributes", which you have shown examples of. But it also has an Attributes property that is "parsed attributes" provided as a TDictionary<string, string>. Have you tried looking into the values of that property yet? You should not need to use a RegEx at all, just enumerate the entries of the TDictionary instead, eg:
var
Attr: TPair<string, string>;
for Attr in Node.Attributes do begin
// use Attr.Key and Attr.Value as needed...
end;
(As the OP asked about using a RegEx to parse attribute=value pairs, this answers the question directly, which other users may be looking for in the future.)
RegEx based answer
Using a RegEx is extremely powerful, from the data you have provided you can extract the attribute name and value pairs using:
(\S+)\s*=\s*(\"?)([^"]*)(\2|\s|$)
This uses grouping and can be explained as follows:
The first result group is the attribute name (it matches non-whitespace characters)
The second result group is an enclosing " if present, otherwise an empty string
The third result group is the value of the attribute
As RegExes can be run recursively you can use MatchAgain to see if there's another match and so read all of the attributes recursively.
procedure ParseAttributes(AInput: String; ATarget: TStringList);
var
LMatched: Boolean;
begin
pRegEx:=TPerlRegEx.Create;
try
pRegEx.RegEx:='(\S+)\s*=\s*(\"?)([^"]*)(\2|\s|$)';
pRegEx.Subject:=AInputData;
LMatched:=pRegEx.Match;
while LMatched do
begin
ATarget.Add(pRegEx.Groups[1].'='+'"'+pRegEx.Groups[3]+'"');
LMatched:=pRegEx.MatchAgain;
end;
finally
pRegEx.Free;
end;
end;
Disclaimer: I haven't tried compiling that code, but hopefully it's enough to get you started!
Practical Point: With respect to the actual problem you posed with your DOM parser - this is a task that there are existing solutions for so a practical answer to solving the problem may well be to use a DOM parser that works! If a RegEx is something you need for whatever reason this one should do the job.

Flask - SQLAlchemy italicizing text imported from db

I have a application that I am pulling data from fields in MySQL database. In some of these fields a some of the text needs to be italicized. For example, in species.html I have a table data "species.Notes" that I am pulling out from the MySQL database. That will render something like this on the page when the application is rendered:
Example:
Tall three tip sagebrush is associated with gray horsebrush (Tetradymia canescens).
The text in parenthesis (i.e. species name) need to be italicized, and the rest needs to remain normal text. I have tried storing the information in the MySQL database with html tags around Tetradymia canescens
<i></i>
<html><i></i></htm>
but that did not work, and I have not been able to find other suggestions online.
For reference (if needed?), here is how the information is being pulled into the html page where it will be displayed.
species.html
<tr>
<th scope="row">Additional Species Information</th>
<td colspan="3">{{ species.Notes }}</td>
</tr>
<tr>
You can use Jinja filters to introduce the italics tag like this:
<td colspan="3">
{{ species.Notes|replace('(', '<i>(')|replace(')', ')</i>')|safe }}
</td>
Ideally you'd preprocess the text with a regex expression and pass it to the template with the tags already there, then you would just need the safe filter alone.

CFdump and Bootstrap tooltips fight each other

I attach Bootstrap tooltips via
$("[title]").tooltip({ html: true });
When I use a <cfdump>, title tags are attached all over the place. The start of the <cfdump> html looks like this
<table class="cfdump_struct">
<tr><th class="struct" colspan="2" onClick="cfdump_toggleTable(this);" style="cursor:pointer;" title="click to collapse">struct</th></tr>
<tr>
<td class="struct" onClick="cfdump_toggleRow(this);" style="cursor:pointer;" title="click to collapse">Cause</td>
<td>
Is there a way to keep, the two from stepping on eachother?
You shouldn't care because cfdump shouldn't be used in production, however you could just reduce the array returned by the jQuery selector. Not sure if this is the best way to do it, but it works:
$("[title]").filter(function(){
return ($(this).closest(".cfdump_struct").length == 0);
}).tooltip({ html: true });
It runs the filter function for each item in the array returned by the selector. If it is within the CFDUMP table (signified by the .cfdump_struct class) it will not return it. You will have to extend this to other cfdump types (queries, etc) but this should get you started.
Again, it really shouldn't matter since you shouldn't be using cfdump in production code anyway.
You can see this in action here: http://jsfiddle.net/seancoyne/rc7TL/

Tabular form validation without submitting

In APEX 3.2, I want to be able to run JavaScript validations to check the data entered and display the appropriate message above each row in the tabular form.
I'm not sure how this would work given that it is a tabular form and the user will be able to add/delete rows.
Appreciate any ideas or suggestions.
Thanks.
Okay, doing some javascript validations on tabular forms is a bit complex, and you need to know what you're doing.
First off, you will need to know the ids or names of the elements you wish to check. As you may know, elements in tabular forms are stored in arrays in apex on submit, and are accessed through apex_application.g_f01/g_f02/...
This is reflected in the html code, and the generated elements also have the attribute 'name' set to the column they belong to. The id also holds the column, plus the rowindex. Warning though, this id is only generated like this when the item is created 'implicitly', ie you did not write your query with apex_item calls (apex_item.textbox(...)).
Another but is that only fields of which the state is saved will have an array column defined. An item which you'd only show as 'display only', will not be generated with an input tag, and will just be held as text in a td tag.
All by all, when you know that, the next steps should be straightforward enough. Take a look at the page source, and take a note of the elements you wish to target. For example, i went for the job field.
<tr class="highlight-row">
<td headers="CHECK$01" class="data"><label for="f01_0003" class="hideMeButHearMe">Select Row</label><input type="checkbox" name="f01" value="3" class="row-selector" id="f01_0003" /></td>
<td headers="EMPNO_DISPLAY" class="data">7782</td>
<td headers="ENAME" class="data"><label for="f03_0003" class="hideMeButHearMe">Ename</label><input type="text" name="f03" size="12" maxlength="2000" value="CLARK" id="f03_0003" /></td>
<td headers="JOB" class="data"><label for="f04_0003" class="hideMeButHearMe">Job</label><input type="text" name="f04" size="12" maxlength="2000" value="MANAGER" id="f04_0003" /></td>
<td headers="HIREDATE" class="data"><label for="f05_0003" class="hideMeButHearMe">Hiredate</label><span style="white-space: nowrap;"><input type="text" id="f05_0003" name="f05" maxlength="2000" size="12" value="09-JUN-81" autocomplete="off"></span></td>
<td headers="SAL" class="data">
<label for="f06_0003" class="hideMeButHearMe">Sal</label><input type="text" name="f06" size="16" maxlength="2000" value="2450" id="f06_0003" />
<input type="hidden" name="f02" value="7782" id="f02_0003" />
<input type="hidden" id="fcs_0003" name="fcs" value="19BD045E01D6BA148B4DEF9DDC8B21B7">
<input type="hidden" id="frowid_0003" name="frowid" value="AAuDjIABFAAAACTAAC" />
<input type="hidden" id="fcud_0003" name="fcud" value="U" />
</td>
</tr>
In the javascript section of the page i then added the following 2 functions.
validate_job does the validation of just one field, the element elJob. The validation i used is just very basic, it's up to you to determine just how complex you want it.
If you want to reference other fields in the same row here, you can do several things: extract the rowindex from the id, if you have it. If it doesn't hold the it, get the parent TR, and then use .children("input[name='f##'") to get an input element in the same row. Or if you need the value of an item which does not save state at all, you'll need to get the TR element, and then find the TD which contains the element you need through the headers attribute, which holds the column name.
function validate_job(elJob){
var sJob = $v(elJob).toUpperCase();
$(elJob).val(sJob);
//do your validations for the field job here
if(sJob=="MANAGER"){
$(elJob).css({"border-color":"red"});
alert("invalid value!");
//depends what you want to do now:
//keep the focus on this element? Set a flag an error occured? Store the error?
return false;
} else {
$(elJob).css({"border-color":""});
alert("value ok");
};
};
Call bind_validations onload. If you allow rows to be created, bind a click event to the addrow button and call bind_validations.
function bind_validations(){
//f01 : row selector
//f03 : ename
//f04 : job
//f05 : hiredate
//f06 : sal
//each input element with attribute name with value f04
//blur event is when the user leaves the field, fe tab out, or even click apply changes
//much like how when-validate-item behaved in forms
$("input[name='f04']").blur(function(){validate_job(this);});
};
Just a proper warning though. I've used javascript validations in some apps so far, but i knew they were only going to be used by a small number of people, and then only internally. It was only one field, with some validations. I made the cursor refocus on the field when the validation failed, so they couldn't jump to the next record and change that aswell. Either a valid value was given, or they reloaded the page or canceled the action. Set up like this, they can't press apply changes either, as the blur event would also fire, validating the field.
When your audience is larger, it gets a bit more iffy: what i javascript is disabled? What if they find some way around? Wizzkids?
I still like the immediate feedback it gives, but in a more critical environment i'd also use the server-side validations. To do this, you need a validation of the type "function returning error text". Check out this page for an example, or this one for some usefull tips (at least for pre 4.0!). Also: apex 4.1 really improves a lot on tabular form validations! ;)

Customizing Django Admin Result List

How can I change the output result from Django Admin Result List? I've been looking into the change_result_list.html template file but all I can find is :
{% for item in result %}{{ item }}{% endfor %}
Which will be outputting something like :
<tr>
<td>
<input type="checkbox" class="action-select" value="2" name="_selected_action" />
</td>
<th>
Lorem Ipsum
</th>
</tr>
Obviously, the th element breaks my design. Is there anyway to change it into td?
There's no "easy" way to do it, so I must first ask why exactly you need to do this at all. I don't see why the <th> element should "obviously" break your design. The <th> element is equivalent to <td>, except that is has extra styling by default (usually font-weight: bold; text-align: center;). You should be able to account for this in your CSS.
That said, here are some avenues to look at. The th/td choice is determined on line 169 (Django 1.2.1) of django.contrib.admin.templatetags.admin_list.py. Here's a snipped summary of the context it appears in:
def items_for_result(cl, result, form):
first = True
for field_name in cl.list_display:
# ...
if (first and not cl.list_display_links) or field_name in cl.list_display_links:
table_tag = {True:'th', False:'td'}[first]
first = False
# ...
yield mark_safe(u'<%s%s><a href="%s"%s>%s</a></%s>' % (table_tag, row_class, url, ...)
else:
# ...
yield mark_safe(u'<td%s>%s</td>' % (row_class, result_repr))
As you can see, there's no obvious way to alter the behaviour that determines the value of table_tag, so some of the options you have are:
Define a "items_for_result" templatetag that calls the one above and replaces any <th>s in the yielded values with <td>s. Then you can override "change_list.html" and {% load %} the tag after "admin_list" is loaded.
Edit the Django code. You'll regret it later though.
If you're OK with none of the columns in the table being a link to the edit page for the item (I can't imagine why you would), you can use this ugly hack in your admin.py:
admin.site.register(YourModel, YourModelAdmin)
admin.site._registry[YourModel].list_display_links = ['not_a_field_name',]`
Since the admin models are only validated once, which happens when register() is called, you can fetch the registered ModelAdmin afterwards and give it an invalid list_display_links property.