Traverse content from other plone site (same installation) ttw?

Hi everyone,

I need to display some collections with content pulled from another Plone site in the same installation.

I know I can use traverse to pull content from another Plone site. But it is not very practical to me, as I need to display pages listing a dozen or more collections of news filtered by categories. I would not like to have to write templates for each one of those pages. Is there an addon to help me do this in a more TTW way?

Also, if I display a traversed collection with item from another site, and the user click on a newsitem, I understand he will be redirected to the other site. This is not what I want, I would like the newsittem to be display as if the user never left the site he is navigating.

Lineage would not help me here, because it is required that the sites are independent (users, database, etc)

Thanks a lot,

Rafael Rocha

I would suggest you to take a look a collective.feedaggregator, a package I developed for similar use cases:

Using collective.feedaggregator does the sites need to be on the same Plone root?

no, collective.feedaggregator lets you add content from whichever site you want; it doesn't need to be Plone-based neither, it only has to have a valid syndication feed in any supported format: RSS 1.0/2.0, Atom…

I just tested it in a fresh Plone 5.0.7 install.

I think the product does not cover exactly my use case, because the user actually leaves the site and visits the content on the original site. I would like to do it as if the user never knew the content was not in this page.

As I understand, this product's functionality is more like the old rss portlet.

Also, I noticed in my test that the content leadimage (already checked it is public) is not being show.

Thanks a lot,

Rafael

I once did that (what I want), but in the old days of Plone 3... I just used traverse to fill the zpt templates. I don't think this is the best approach now.

I am trying to do what I did before to make it work in Plone 4. I am TTW customizing the template plone.dexterity.interfaces.idexteritycontainer-listing_view. I copied it to the folder I want this behaviour.

I edited the line
<tal:results define="batch view/batch">

and changed it to:

<tal:results define="batch python:context.othersite.portal_catalog.searchResults({'portal_type': 'News Item', 'review_state': 'published'});">

This worked before. Instead of rendering the content of the folder, it would return this search that uses traverse to get content from othersite.

When the query return empty I get no error, just an empty space.

When the query returns something, I get the following traceback. Can someone help me with this? Is this a permission problem?:

Traceback (innermost last):

Module ZPublisher.Publish, line 138, in publish
Module ZPublisher.mapply, line 77, in mapply
Module ZPublisher.Publish, line 48, in call_object
Module five.customerize.zpt, line 83, in __call__
Module Products.PageTemplates.ZopePageTemplate, line 338, in _exec
Module Products.PageTemplates.ZopePageTemplate, line 435, in pt_render
Module Products.PageTemplates.PageTemplate, line 87, in pt_render
Module zope.pagetemplate.pagetemplate, line 132, in pt_render
Module five.pt.engine, line 98, in __call__
Module z3c.pt.pagetemplate, line 163, in render
Module chameleon.zpt.template, line 261, in render
Module chameleon.template, line 191, in render
Module chameleon.template, line 171, in render
Module 6f2dfff5e8f3e18631d996f2463a2b0d.py, line 1651, in render
Module 64de3a14df28dbc69b12f555c12ec8dd.py, line 1223, in render_master
Module 64de3a14df28dbc69b12f555c12ec8dd.py, line 420, in render_content
Module 6f2dfff5e8f3e18631d996f2463a2b0d.py, line 1639, in __fill_content_core
Module 6f2dfff5e8f3e18631d996f2463a2b0d.py, line 440, in render_content_core
Module 6f2dfff5e8f3e18631d996f2463a2b0d.py, line 293, in render_listing
Module 6f2dfff5e8f3e18631d996f2463a2b0d.py, line 1339, in render_entries
Module five.pt.expressions, line 135, in __call__
Module five.pt.expressions, line 126, in traverse
Module zope.traversing.adapters, line 136, in traversePathElement
__traceback_info__: (<Products.ZCatalog.Catalog.mybrains object at 0x7fcea75d8328>, 'PortalType')
Module zope.traversing.adapters, line 50, in traverse
__traceback_info__: (<Products.ZCatalog.Catalog.mybrains object at 0x7fcea75d8328>, 'PortalType', ())

LocationError: (<Products.ZCatalog.Catalog.mybrains object at 0x7fcea75d8328>, 'PortalType')

  • Expression: "item/PortalType"
  • Filename: ... ne.dexterity.interfaces.idexteritycontainer-listing_view
  • Location: (line 31: col 21)
  • Arguments: repeat: {...} (0)
    template: <ImplicitAcquisitionWrapper plone.dexterity.interfaces.idexteritycontainer-listing_view at 0x7fcea7512d20>
    modules: <instance - at 0x7fcf15ba4170>
    here: <ImplicitAcquisitionWrapper orgao-central at 0x7fcea7512820>
    user: <ImplicitAcquisitionWrapper - at 0x7fcea75124b0>
    nothing: <NoneType - at 0x7ac030>
    target_language: <NoneType - at 0x7ac030>
    container: <ImplicitAcquisitionWrapper orgao-central at 0x7fcea7512820>
    default: <object - at 0x7fcf202ddc50>
    request: <instance - at 0x7fcef9a34758>
    wrapped_repeat: <SafeMapping - at 0x7fcea761fe68>
    loop: {...} (1)
    context: <ImplicitAcquisitionWrapper orgao-central at 0x7fcea7512820>
    translate: <function translate at 0x7fcedd173398>
    root: <ImplicitAcquisitionWrapper Zope at 0x7fcebdaaa500>
    options: {...} (1)
    view: <TTWView None at 0x7fcea758b8d0>

The error information above tells you that the problem is in the expression item/PortalType and that it is a LocationError. This means, that item does not have PortalType. item appears to be a so called brain, i.e. a catalog proxy object returned from catalog searches. It has the attributes defines as metadata in the corresponding catalog. Check your catalog definition.

I corrected it to portal_type and this error was solved.

python:context.othersite.portal_catalog.searchResults({'portal_type': 'News Item', 'review_state': 'published'});">

Now I am getting an error about batch multipages and the traverse adapter (pasted traceback below). Again another location error. Multipage is not a catalog index.

But looking at this traceback I can't find which line of my template is raising the error, only the line of code in batchnavigation.pt.

I think I am again trying to use a query catalog result as a batch object. Is there any way to do this conversion in the template?

<Products.ZCatalog.Catalog.mybrains object at 0x7fcea693e530>, <Products.ZCatalog.Catalog.mybrains object at 0x7fcea693e598>, <Products.ZCatalog.Catalog.mybrains object at 0x7fcea693e600>], 'multiple_pages')

  • Expression: "python:batchnavigation(batch, batchformkeys)"
  • Filename: ... .batching-1.1.1-py2.7.egg/plone/batching/batch_macros.pt
  • Location: (line 15: col 31)
  • Source: ... "structure python:batchnavigation(batch, batchformkeys)" />
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  • Expression: "batch/multiple_pages"
  • Filename: ... tching-1.1.1-py2.7.egg/plone/batching/batchnavigation.pt
  • Location: (line 8: col 21)
  • Source: tal:condition="batch/multiple_pages">
    ^^^^^^^^^^^^^^^^^^^^
  • Arguments: repeat: {...} (0)
    template: <ViewPageTemplateFile - at 0x7fceefc38bd0>
    views: <ViewMapper - at 0x7fcea72bee50>
    modules: <instance - at 0x7fcf15ba4170>
    args: <tuple - at 0x7fcf20307050>
    here: <ImplicitAcquisitionWrapper orgao-central at 0x7fcea6e60cd0>
    user: <ImplicitAcquisitionWrapper - at 0x7fcedf276050>
    nothing: <NoneType - at 0x7ac030>
    container: <ImplicitAcquisitionWrapper orgao-central at 0x7fcea6e60cd0>
    request: <instance - at 0x7fcea68b7170>
    wrapped_repeat: <SafeMapping - at 0x7fce97bcfba8>
    traverse_subpath: <list - at 0x7fcea67f3440>
    default: <object - at 0x7fcf202ddc50>
    loop: {...} (0)
    context: <ImplicitAcquisitionWrapper orgao-central at 0x7fcea6e60cd0>
    view: <SimpleViewClass from /home/rc/Plone-inverta/buildout-cache/eggs/plone.batching-1.1.1-py2.7.egg/plone/batching/batchnavigation.pt batchnavigation at 0x7fcea72beed0>
    translate: <function translate at 0x7fce97bcb2a8>
    root: <ImplicitAcquisitionWrapper Zope at 0x7fceae522370>
    options: {...} (0)
    target_language: <NoneType - at 0x7ac030>

Just made it work.

<tal:results define="Batch python:modules['Products.CMFPlone'].Batch; batch python:Batch(context.othersite.portal_catalog.searchResults({'portal_type': 'News Item', 'review_state': 'published'}), 5, 1, orphan=0);">

Gotta still do some tweaks as sort order.

It would be nice though if we could use collections to traverse other sites in the same root.
Thabks,

Rafael

Now, as I described in my first post, I want that when the user click on one of the listed items, he sees this content inside the site he is visiting, never noticing the content came from another site.

In my previous Plone 4 site I did it like this. In my listing_template I also changed item_url to:

item_url python:'http://oldsite.org:8080/newsite/reading-from-outside?editorial='+item.id;

Then I had a Page Template called reading-from-outside with the following content:

    <HTML metal:use-macro="here/main_template/macros/master">
    <HEAD>
    </HEAD>
    <BODY metal:fill-slot="main">
    <DIV tal:define="arq python:context.othersite.portal_catalog.searchResults(portal_type='News Item', Subject ='editorial',sort_on='effective', id=context.REQUEST.editorial)[0].getObject()">
    <DIV tal:replace="structure python:arq.CookedBody(stx_level=2)"></DIV></DIV>
    </BODY>
    </HTML>

This used to work. But now when I try to access an item in this way the page keeps loading then after some minutes is left blank, with no timeout error displayed.

What has changed here between plone versions?

I am wondering if this is a use case to be solved with theming? What do you think?

Thanks,

Rafael

CookedBody was defined by Products.CMFDefault.Document.Document (overridden by Products.ATContentTypes.content.ATDocumentBase). The newer plone.dexterity does not derive from CMFDefault and corresponding content types likely lack the CookedBody method.

I assume that accessing CookedBody is the cause for your problem (unless you continue to use Archetypes based content). I do not know why you do not get an AttributeError (sometimes mapped to NotFound).

I would explore what happens in an interactive Python session (bin/client1 debug).

Hi everyone,

Digging a little bit more I edited the template and did what I wanted. let me share my final solution:

1 - Created a folder

2 - Customized plone.dexterity.interfaces.idexteritycontainer-listing_view in Viewlets_customization and moved it to my folder. This way only this folder (and what is inside) will use this customizations...

3 - Edit it to:

<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"
    xmlns:tal="http://xml.zope.org/namespaces/tal"
    xmlns:metal="http://xml.zope.org/namespaces/metal"
    xmlns:i18n="http://xml.zope.org/namespaces/i18n"
    lang="en"
    metal:use-macro="context/main_template/macros/master"
    i18n:domain="plone">
<body>

<metal:content-core fill-slot="content-core">
<metal:block define-macro="content-core">

  <div metal:define-macro="text-field-view"
      id="parent-fieldname-text" class="stx"
      tal:define="text view/text"
      tal:condition="text"
      tal:attributes="class view/text_class">
    <div metal:define-slot="inside" tal:replace="structure text">The body</div>
  </div>

  <metal:listingmacro define-macro="listing">
   <tal:results define="Batch python:modules['Products.CMFPlone'].Batch; batch python:Batch(context.othersite.portal_catalog.searchResults(portal_type='News Item', Subject ='editorial', sort_on='effective', sort_order='reverse'), 30, 0, orphan=0);">
      <tal:listing condition="batch">
        <div class="entries" metal:define-slot="entries">
          <tal:repeat repeat="item batch" metal:define-macro="entries">
            <tal:block tal:define="obj item/getObject;
                item_url item/getURL;
                item_id item/getId;
                item_title item/Title;
                item_description item/Description;
                item_type item/portal_type;
                item_modified item/ModificationDate;
                item_created item/CreationDate;
                item_type_class python:'contenttype-' + view.normalizeString(item_type);
                item_wf_state item/review_state;
                item_wf_state_class python:'state-' + view.normalizeString(item_wf_state);
                item_creator item/Creator;
                item_link python:'https://newsite.org/othersite/newsite/folder/reading-from-outside?editorial='+item.id;
                item_is_event python:view.is_event(obj);
                item_has_image python:item.getIcon">
              <metal:block define-slot="entry">
                <article class="entry">
                  <header metal:define-macro="listitem" tal:attributes="class python:'vevent' if item_is_event else None">
                    <span class="summary" tal:attributes="title item_type">
                     <a tal:attributes="href item_link">
                      <img class="image-tile"
                             tal:condition="item_has_image"
                             tal:attributes="src  string:$item_url/@@images/image/tile">
                      </a>
                      <a tal:attributes="href item_link;
                                         class string:$item_type_class $item_wf_state_class url;
                                         title item_type"
                          tal:content="item_title">

                             Item Title
                      </a>
                     </span>
                    <metal:block metal:define-macro="document_byline">
                      <div class="documentByLine">
                        <tal:event condition="item_is_event">
                          <tal:date tal:replace="structure python:view.formatted_date(obj)"/>
                          <span tal:condition="item/location" i18n:translate="label_event_byline_location">&mdash;
                            <span tal:content="string:${item/location}" class="location" i18n:name="location">Oslo</span>,
                          </span>
                        </tal:event>
                        <tal:byline condition="view/show_about">
                          &mdash;
                          <tal:name tal:condition="item_creator"
                              tal:define="author python:view.pas_member.info(item_creator);
                                          creator_short_form author/username;
                                          creator_long_form string:?author=${author/username};
                                          creator_is_openid python:'/' in creator_short_form;
                                          creator_id python:(creator_short_form, creator_long_form)[creator_is_openid];">
                          <span i18n:translate="label_by_author">
                            by
                            <a tal:attributes="href string:${view/navigation_root_url}/author/${item_creator}"
                                tal:content="author/name_or_id"
                                tal:omit-tag="not:author"
                                i18n:name="author">
                              Bob Dobalina
                            </a>
                          </span>
                          </tal:name>

                          <tal:modified condition="python: item_type != 'Event'">
                            &mdash;
                            <tal:mod i18n:translate="box_last_modified">last modified</tal:mod>
                            <span tal:replace="python:view.toLocalizedTime(item_modified,long_format=1)">
                              August 16, 2001 at 23:35:59
                            </span>
                          </tal:modified>

                          <metal:description define-slot="description_slot">
                            <tal:comment replace="nothing">
                              Place custom listing info for custom types here
                            </tal:comment>
                          </metal:description>
                        </tal:byline>
                      </div>
                    </metal:block>
                  </header>
                  <p class="description discreet"
                      tal:condition="item_description"
                      tal:content="item_description">
                    description
                  </p>
                </article>
              </metal:block>
            </tal:block>
          </tal:repeat>
        </div>

        <div metal:use-macro="context/batch_macros/macros/navigation" />

      </tal:listing>

      <metal:empty metal:define-slot="no_items_in_listing">
        <p class="discreet"
            tal:condition="not: view/batch"
            tal:content="view/no_items_message">
          There are currently no items in this folder.
        </p>
      </metal:empty>

    </tal:results>
  </metal:listingmacro>

</metal:block>
</metal:content-core>

</body>
</html>

4 - Created a page template named reading-from-outside with the following content

<HTML metal:use-macro="here/main_template/macros/master">
<HEAD>
</HEAD>

<body metal:fill-slot="main">
<DIV tal:define="arq python:context.othersite.portal_catalog.searchResults(portal_type='News Item', Subject ='editorial',sort_on='effective', id=context.REQUEST.editorial)[0].getObject()">
<metal:title define-slot="content-title">
            <h1 class="documentFirstHeading"
                tal:define="title arq/Title"
                tal:condition="title"
                tal:content="title">Title or id</h1>
 </metal:title>

 <div class="documentDescription description"
                 tal:define="description arq/Description"
                 tal:content="description"
                 tal:condition="description">
                Description
</div>


  <div id="parent-fieldname-text"
  tal:condition="arq/getText"
  tal:content="structure arq/getText" />

</DIV>
</body>
</HTML>

This way, one someone enters this folder, it ill list all the news_items with the editorial tag from the other site... It also modifies the link of them, so the user won't get redirected outside the site he is in. He will go to this reading-from-outside template that will receive a parameter through the url and make a new catalog query to finally display the fields of this item.

I could not get the image, though as it is outside the template...

Also, I found the url a bit strange. To display in the newsite a content from the othersite, rendered in the newsite context, I had to use..

https://newsite.org/othersite/newsite/

But it works! I have no clue about performance issue, but it does not seems to be slow.

Thanks for everyone that helped.

ps - Does anyone thinks it would be nice to make a product from this. I can give some time, but I don't know about security, performance, etc issues that this approach disconsiders..

1 Like

Maybe a tile from mosaic, that would return a collection, but where the site admin could chose the context of the catalog...

What happened when you omitted otherside/newsite?

As potential reason for the necessity of this url extension, I see traversal "magic": Plone is using (url) traversal hooks to set up various things during url traversal into a Plone portal. For example, the layer associated with a portal is activated in this way. With your extended urls, the hooks for both "oldsite" as well as "newsite" are activated; otherwise only those of "newsite".

There are ways to execute foreign traversal hooks in a programmatic way (without the need to artificially change the url). Note however, that Plone is not designed that more than a single portal context is activated. This means, you may face surprises when you "activate" more than a single portal (whether by extended urls or programmatically).

If I omit the /othersite/ the content is not pulled and I get a "Page doesn't exist" error

If I omit the /newsite/ after it, the content is pulled, but is displayed in othersite context, with its look (logo, css, etc...).

What kind of surprises may I face? Nothing that would corrupt my data, right?

This is strange (as the page itself does not depend on the content). I would temporarily reconfigure your "error_log" object (not to ignore "Not Found") and see where the exception really comes from. The othersite catalog should be able to locate (= find) the content even without the url mangling.

That is to be expected - as the latest traversal hooks have a good chance to override earlier effects.

That infrastructure from more than a single portal is active and may kick in at inappropriate places. E.g. it might be possible the othersite users are able to login in newsite. Or that overrides in the othersite layer become effective in newsite as well.

I would not expect data corruption.

You are correct, sorry. My mistake! I am the stupid who did not keep it simple :wink:

So, Is traverse a security problem? Is it possible to turn it off? Is it mandatory that if I wan't to keep two sites isolated, i must have different plone installations for them?

I imagine if I had two clients, and accidentaly someone types a url with the name of the other site and get the other's client side inside the initial. This would be very embarassing..

You may find interesting to check: