Search code examples
templatesviewplonedexterity

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


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:

  1. 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?
  2. 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.


Solution

  • 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:

    1. 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 &lt; 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>
    
    1. 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.