I'm working with the sitepackage by Ben Kott for Typo3 9.5 and include content into my fluid template like this:
<f:cObject typoscriptObjectPath="lib.dynamicContent" data="{colPos: '1'}" />
I'm tryin to wrap this into a condition withing fluid like
<f:if condition="<f:cObject typoscriptObjectPath='lib.dynamicContent' data='{colPos: \'1\'}'">
whatever
</f:if>
it does not work though. I can not tell if there's anything wrong syntax or if it's not possible.
{f:cObject(typoscriptObjectPath: 'lib.dynamicContent', data: {colPos: 1}) -> f:variable(name: 'content')}
<f:if condition="{content}">
There is content. Here it is:
{content -> f:format.raw()}
</f:if>
Avoids double rendering of the typoscript object, double DB requests etc.
Avoids tag syntax inside tag attributes which is probably going to be impossible to do in future Fluid versions
Edit for posterity: the exact reason why the code above failed seems to be a syntax error:
<f:if condition="<f:cObject typoscriptObjectPath='lib.dynamicContent' data='{colPos: \'1\'}'">
Should be:
<f:if condition="<f:cObject typoscriptObjectPath='lib.dynamicContent' data='{colPos: \'1\'}' />">
Since the inner tag was not closed. You should still avoid it though - use inline syntax instead. In the code I put above you can remove the -> f:variable() part and the expression can then be used as tag attribute value.
Another solution would be a dedicated TypoScript object which you can use in Fluid if conditions
################################################
#### COUNT CONTENT LIB FOR USAGE IN FLUID ####
################################################
#
# EXAMPLE: amount of content elements in colPos 1 of actual PID
# ---------------
# <f:cObject typoscriptObjectPath="lib.countContent" data="{colPos: 1}" />
# {f:cObject(typoscriptObjectPath: 'lib.countContent', data: '{colPos: 1}')}
#
# EXAMPLE: amount of content elements in more than one colPos of actual PID
# ---------------
# <f:cObject typoscriptObjectPath="lib.countContent" data="{colPos: '1,2'}" />
# {f:cObject(typoscriptObjectPath: 'lib.countContent', data: '{colPos: \'1,2\'}')}
#
#
#
#
# Usage examples:
# --------------
#
# <f:if condition="{f:cObject(typoscriptObjectPath: 'lib.countContent', data: '{colPos: 1}')}">
# <f:cObject typoscriptObjectPath="lib.dynamicContent" data="{pageUid: '{data.uid}', colPos: '1', wrap: '<aside class=\"l-aside\">|</aside>'}" />
# </f:if>
#
#
# <f:if condition="{f:cObject(typoscriptObjectPath: 'lib.countContent', data: '{colPos: 1}')}">
# <aside class="l-aside">
# <f:cObject typoscriptObjectPath="lib.dynamicContent" data="{colPos: '1'}" />
# </aside>
# </f:if>
#
#
###############
lib.countContent = COA
lib.countContent {
5 = LOAD_REGISTER
5 {
colPos.cObject = TEXT
colPos.cObject {
field = colPos
ifEmpty.cObject = TEXT
ifEmpty.cObject {
value.current = 1
ifEmpty = 0
}
}
pageUid.cObject = TEXT
pageUid.cObject {
field = pageUid
ifEmpty.data = TSFE:id
}
contentFromPid.cObject = TEXT
contentFromPid.cObject {
data = DB:pages:{register:pageUid}:content_from_pid
data.insertData = 1
}
}
20 = CONTENT
20 {
table = tt_content
select {
selectFields = count(uid) AS counter
where = {#colPos} IN({register:colPos})
where.insertData = 1
pidInList.data = register:pageUid
pidInList.override.data = register:contentFromPid
andWhere = (deleted = 0 AND hidden = 0)
}
renderObj = COA
renderObj {
10 = TEXT
10 {
data = field:counter
}
}
}
90 = RESTORE_REGISTER
}
This snippet is tested and used in TYPO3 8.7 LTS without workspaces
I have rigged up a custom "Advanced Search" view by slightly extending the flask admin index view. This takes the user to a custom form rendered using the flask admin form rendering machinery to do most of the work.
The form is defined as follows:
class PaperSearchForm(FlaskForm):
return_url = HiddenField()
title = StringField()
abstract = StringField()
start_date = DateTimeField()
end_date = DateTimeField()
doi = StringField()
pubmed_id = StringField()
link = StringField()
journals = QuerySelectMultipleField(
query_factory=_get_model(Journal),
)
authors = QuerySelectMultipleField(
query_factory=_get_model(Author),
)
keywords = QuerySelectMultipleField(
query_factory=_get_model(Keyword),
)
chapters = QuerySelectMultipleField(
query_factory=_get_model(Chapter),
)
printed = BooleanField(default=True)
unprinted = BooleanField(default=True)
submit = SubmitField('Search')
The Advanced Search model view is defined like this:
from flask import flash
from flask import redirect
from flask import request
from flask_admin import BaseView
from flask_admin import expose
from flask_wtf import FlaskForm
from flask_login import current_user
from .forms import PaperSearchForm
class AdvancedPaperSearchView(BaseView):
form_base_class = FlaskForm
def __init__(self,
name=None,
category=None,
endpoint=None,
url=None,
template='auth/model/paper/advanced_search.html',
menu_class_name=None,
menu_icon_type=None,
menu_icon_value=None
):
super(AdvancedPaperSearchView, self).__init__(
name,
category,
endpoint,
url or '/',
'static',
menu_class_name=menu_class_name,
menu_icon_type=menu_icon_type,
menu_icon_value=menu_icon_value)
self._template = template
def is_visible(self):
return False
def is_accessible(self):
if current_user.is_authenticated:
return current_user.can_view_papers()
return False
#expose('/', methods=['GET', 'POST'])
def index(self):
form = PaperSearchForm()
form.return_url.data = request.args['return_url']
self._template_args['form'] = form
self._template_args['cancel_url'] = request.args['return_url']
return self.render(self._template)
#expose('/search', methods=['POST'])
def search(self):
# List view generates list of models based on 'term'= from request.args.get('term', default=None)
# Manually setting these arguments will serve as the advanced search functionality
form = PaperSearchForm() # ???
search = None # ???
filter = None # ???
flash('How to apply multiple filters?', 'error')
return redirect('papermodelview.index', search=search, filter=filter) # ???
Then, the template is defined like this:
{% extends "admin/master.html" %}
{% import 'admin/lib.html' as lib with context %}
{% from 'admin/lib.html' import extra with context %} {# backward compatible #}
{% from 'admin/lib.html' import render_field with context %}
{% block head %}
{{ super() }}
{{ lib.form_css() }}
{% endblock %}
{% block body %}
{% block navlinks %}
<ul class="nav nav-tabs">
<li>
List
</li>
<li class="active">
Advanced Search
</li>
</ul>
{% endblock %}
<form method="post" action="{{ url_for('advancedpapersearchview.search') }}">
{{ form.return_url }}
{{ form.csrf_token }}
{{ render_field(form, form.title) }}
{{ render_field(form, form.abstract) }}
{{ render_field(form, form.start_date) }}
{{ render_field(form, form.end_date) }}
{{ render_field(form, form.doi) }}
{{ render_field(form, form.pubmed_id) }}
{{ render_field(form, form.link) }}
{{ render_field(form, form.journals) }}
{{ render_field(form, form.authors) }}
{{ render_field(form, form.chapters) }}
{{ render_field(form, form.keywords) }}
{{ render_field(form, form.printed) }}
{{ render_field(form, form.unprinted) }}
<div class="row">
<div class="col-xs-12">
{{ form.submit(class="btn") }}
</div>
</div>
<div class="row">
<div class="col-xs-12">
<a href="{{ cancel_url }}" class="btn warning">
Cancel
</a>
</div>
</div>
</form>
{% endblock %}
{% block tail %}
{{ super() }}
{{ lib.form_js() }}
<script src="/static/vendor/jquery.min.js" type="text/javascript">/script>
{# use /static/bootstrap2/js/bootstrap.min.js if you are using bootstrap2 #}
<script src="/static/bootstrap3/js/bootstrap.min.js" type="text/javascript"></script>
<script src="/static/vendor/moment.min.js" type="text/javascript"></script>
<script src="/static/vendor/select2/select2.min.js" type="text/javascript"></script>
{% endblock %}
In the Paper Model View, the filters are defined like this:
class PaperModelView(MainModelView):
# ...
column_filters = [
'chapter_paper_assoc.printed',
'journal_paper_assoc.publication_date',
'chapters.name',
'chapters.number',
'journals.name',
'authors.last_name',
'keywords.keyword',
]
So, I commented a bunch of ??? where I don't know what to do. How do I map the fields of my form (specific attributes of the models selected) to the filters that are defined by 'column_filters' in the model view.
I.e -- rather than overriding the index view search handling to actually perform a search, I could instead apply a bunch of filters by passing this information to the index view, which retrieves this information with:
filters=response.args.get('filter', None)
Is there a better approach?
Thanks
Alright, things are about to get ugly, so hold onto your hat.
This is what I ended up writing to accomplish this functionality, and it is code that I am not particularly proud to have written. It works, but that said, please suggest a cleaner way to do this if you feel like it.
Here is the advanced search view:
from flask import flash
from flask import redirect
from flask import request
from flask import url_for
from flask_admin import BaseView
from flask_admin import expose
from flask_wtf import FlaskForm
from flask_login import current_user
from app import admin
from .forms import PaperSearchForm
class AdvancedPaperSearchView(BaseView):
form_base_class = FlaskForm
def __init__(self,
name=None,
category=None,
endpoint=None,
url=None,
template='auth/model/paper/advanced_search.html',
menu_class_name=None,
menu_icon_type=None,
menu_icon_value=None
):
super(AdvancedPaperSearchView, self).__init__(
name,
category,
endpoint,
url or '/',
'static',
menu_class_name=menu_class_name,
menu_icon_type=menu_icon_type,
menu_icon_value=menu_icon_value)
self._template = template
def is_visible(self):
return False
def is_accessible(self):
if current_user.is_authenticated:
return current_user.can_view_papers()
return False
#expose('/', methods=['GET', 'POST'])
def index(self):
form = PaperSearchForm()
form.return_url.data = request.args['return_url']
self._template_args['form'] = form
self._template_args['cancel_url'] = request.args['return_url']
return self.render(self._template)
#expose('/search', methods=['POST'])
def search(self):
form = PaperSearchForm()
# The goal here is to get the paper model view from the currently running app (and its admin extension). Once
# the model view is here, use it to get the available filters (get their keys and operations). Use the existing
# request args and add filters to them using the key and operations defined in the model view.
paper_model_view = None
for view in admin._views:
# There must be a better way to do this, and I know this is a WTF, but I don't have the vocabulary to search
# the flask admin documentation for the right way to get the instance of the model view from the admin
# object. I need the *instance*, with the filters created and added to that instance by the init... so...
# not clean or pretty ... and permanently restricts name of paper model view ... TODO: Fix? Rewrite?
# - Chris, March 2017
if "PaperModelView object" in view.__repr__():
paper_model_view = view
# ._filters contains the list of all filters
# ._filter_args contains a dictionary of keys and filter objects for url construction
# each filter is persisted with request.args, the query string is <flt[position]_[key]=[value]> or
# <flt[position]_[key]> for filters without values
# The filter is accessed by looking up the filter object with the key value, and then the filters are listed
# in the order of the position provided in the query string. I am unsure whether or not they are applied in
# this order, but that seems like what is happening.
filters = {}
i = 0
str = "flt{pos}_{key}"
def __check(column, table, filter):
return (column in filter.column.name and table in filter.column.table.name.__repr__())
# Sorry for this...
# Iterate through each filter available for the view. Check if it's name and operation are something that
# will enact a portion of the search, then add it's filter (in the format expected) to a dictionary. The index
# variable i keeps track of the "count" of filters that have been added and uses this as the position of the
# filter.
for key, key_filter in paper_model_view._filter_args.items():
filter = key_filter[1]
if hasattr(filter, 'column'):
if __check("title", "papers", filter):
if "FilterLike" in filter.operation.__repr__():
if form.title.data:
filters[str.format(pos=i, key=key)] = form.title.data
i += 1
if __check("abstract", "papers", filter):
if "FilterLike" in filter.operation.__repr__():
if form.abstract.data:
filters[str.format(pos=i, key=key)] = form.abstract.data
i += 1
if __check("publication_date", "journal_paper", filter):
if "DateSmaller" in filter.operation.__repr__():
if form.end_date.data:
filters[str.format(pos=i, key=key)] = form.end_date.data.date() # Only keeps the date for the filter
i += 1
elif "DateGreater" in filter.operation.__repr__():
if form.start_date.data:
filters[str.format(pos=i, key=key)] = form.start_date.data.date()
i += 1
if __check("doi", "papers", filter):
if "FilterLike" in filter.operation.__repr__():
if form.doi.data:
filters[str.format(pos=i, key=key)] = form.doi.data
i += 1
if __check("pubmed_id", "papers", filter):
if "FilterLike" in filter.operation.__repr__():
if form.pubmed_id.data:
filters[str.format(pos=i, key=key)] = form.pubmed_id.data
i += 1
if __check("link", "papers", filter):
if "FilterLike" in filter.operation.__repr__():
if form.link.data:
filters[str.format(pos=i, key=key)] = form.link.data
i += 1
if __check("name", "journal", filter):
if "FilterLike" in filter.operation.__repr__():
if form.journals.data:
for journal in form.journals.data:
filters[str.format(pos=i, key=key)] = journal.name
i += 1
if __check("first_name", "authors", filter):
if "FilterLike" in filter.operation.__repr__():
for author in form.authors.data:
filters[str.format(pos=i, key=key)] = author.first_name
i += 1
if __check("last_name", "authors", filter):
if "FilterLike" in filter.operation.__repr__():
for author in form.authors.data:
filters[str.format(pos=i, key=key)] = author.last_name
i += 1
if __check("keyword", "keywords", filter):
if "FilterLike" in filter.operation.__repr__():
for keyword in form.keywords.data:
filters[str.format(pos=i, key=key)] = keyword.keyword
i += 1
if __check("name", "chapters", filter):
if "FilterLike" in filter.operation.__repr__():
for chapter in form.chapters.data:
filters[str.format(pos=i, key=key)] = chapter.name
i += 1
if __check("printed", "chapter_paper", filter):
if "FilterEqual" in filter.operation.__repr__():
if form.printed.data == True:
if form.unprinted.data == False: # Printed only
filters[str.format(pos=i, key=key)] = 1 # True
i += 1
else:
pass # Both are True
else:
if form.unprinted.data == True: # Unprinted only
filters[str.format(pos=i, key=key)] = 0 # False
i += 1
else:
pass # Both are False
else:
continue
flash('Filters successfully applied', 'success')
return redirect(url_for('paper.index_view', **filters))
I have this typoscript to request content only (without the header, css, js, etc)
ajaxCall = PAGE
ajaxCall {
typeNum = 999
config.disableAllHeaderCode = 1
config.disablePrefixComment = true
# config.additionalHeaders = Content-type: text/html; charset=utf-8
config.metaCharset = UTF-8
10 = COA
10 < styles.content.get
10.stdWrap.prepend > # supress feEditAdvanced-firstWrapper - Bug in typo3 4.3.1
}
This works fine as long as I am in the default language. But when I want to use this pageType on another language &L=1 it does not work and I get nothing.
I tested around a bit and found out that the problem is here
10 = COA
10 < styles.content.get
It looks like when there is a language parameter the styles.content.get is empty.
Edit: I try to get the content with my own CONTENT object
ajaxCallw = PAGE
ajaxCallw {
typeNum = 1000
config.disableAllHeaderCode = 1
config.disablePrefixComment = true
# config.additionalHeaders = Content-type: text/html; charset=utf-8
config.metaCharset = UTF-8
10 = CONTENT
10 {
table = tt_content
select.orderBy = sorting
select.where = colPos=0
select.languageField = 4
}
}
It looks like select.languageField = 4 gets completly ignored, as it still displays me the content from the default langauge. And when I call a URL with &type=1000&L=4 it still does not show me anything. (Without the L=4 parameter it shows me the default language content)
select.languageField is a pointer to the field in the database
select.languageField = sys_language_uid
As far as I remember the language variable is not parsed by default, so you have to set it yourself
ajaxCall {
# your code
config.sys_language_uid = 0
}
# condition for the language. Adjust it to your language id
[globalVar = GP:L = 1]
ajaxCall.config.sys_language_uid = 1
[global]
The sys_language_overlay = hideNonTranslated was the problem.
Adding: ajaxCall.config.sys_language_overlay = 0 solved the problem.
ajaxCall = PAGE
ajaxCall {
typeNum = 999
config.disableAllHeaderCode = 1
config.disablePrefixComment = true
# config.additionalHeaders = Content-type: text/html; charset=utf-8
config.metaCharset = UTF-8
config.sys_language_overlay = 0
10 = COA
10 < styles.content.get
10.stdWrap.prepend > # supress feEditAdvanced-firstWrapper - Bug in typo3 4.3.1
}
I am not sure why exactly this works now..
styles.content.get is just a CONTENT object that gets the content of col0 (rendered true or with css_styled_content) you can better just fetch your content with your own CONTENT object, thus you can have more control of it.
Today I have done one typo3 upgrade from 4.5 to 6.2. Initially from 4.5 to 4.7 everything was working fine with out any issues. After upgrading TYPO3 6.2 content elements like textimage and image only are not rendering . Can any one please tell me why this is happening? The below given typoscript is used to rendering banner images in the website. Can any one give me a hint?
#####################
### lib.keyVisual #
#####################
lib.keyVisual = COA
lib.keyVisual {
5 = LOAD_REGISTER
5.maxImageWidth = 960
5.maxImageWidthInText = 960
50 = RECORDS
50.source.current = 1
50.tables = tt_content
# remove all divs from textpic rendering
50.conf.tt_content.stdWrap.innerWrap.cObject.default.15.value = item
50.conf.tt_content.textpic.20.layout >
50.conf.tt_content.textpic.20.layout = TEXT
50.conf.tt_content.textpic.20.layout.value = ###IMAGES######TEXT###
50.conf.tt_content.textpic.20.imageStdWrap.dataWrap =
50.conf.tt_content.textpic.20.imageStdWrapNoWidth.dataWrap =
50.conf.tt_content.textpic.20.imageColumnStdWrap.dataWrap =
50.conf.tt_content.textpic.20.rendering.simple.imageStdWrapNoWidth.dataWrap =
50.conf.tt_content.textpic.20.rendering.simple.imageStdWrap.dataWrap =
50.conf.tt_content.textpic.20.rendering.simple.imageStdWrapNoWidth.wrap =
50.conf.tt_content.textpic.20.rendering.dl.imageStdWrapNoWidth.dataWrap =
50.conf.tt_content.textpic.20.rendering.dl.imageStdWrap.dataWrap =
50.conf.tt_content.textpic.20.rendering.dl.imageStdWrapNoWidth.wrap =
50.conf.tt_content.textpic.20.text.wrap.cObject = CASE
50.conf.tt_content.textpic.20.text.wrap.cObject {
key.field = imageorient
key.stdWrap.wrap = |+1
key.prioriCalc = 1
1 = TEXT
1.value = <div class="item-position1"> | </div>
2 = TEXT
2.value = <div class="item-position2"> | </div>
9 = TEXT
9.value = <div class="item-position3"> | </div>
4 = TEXT
4.value = <div class="item-position4"> | </div>
default = TEXT
default.value = <div class="item-position-default item-position-{field:imageorient}"> | </div>
default.insertData = 1
}
100 = RESTORE_REGISTER
wrap.required = 1
wrap (
<div id="page-hero" class="page-hero">
<div class="slides_container">
|
</div>
<a class="prev" href="#"></a>
<a class="next" href="#"></a>
</div>
)
}
BR
Siva
It's probably the function IMAGE($conf) that fails to render it. You can debug it in typo3/sysext/frontend/Classes/ContentObject/ContentObjectRenderer.php
If it's not that exact one it might be IMGTEXT or a similar one.
After an upgrade from TYPO3 4.x to 6.2 you must perform several steps to get the images back.
Install Tool - Configuration presets: Image handling settings -> Use the detected (marked green) Image Magick or Graphics Magick
Install Tool - Test Setup and make some of the image tests
Install Tool - Clean Up: cache_imagesizes
Install Tool - Clean Up: Clear processed files
TYPO3 Backend - File Module: Click on the Filelist. This will load all images found there into the File Abstraction Layer (FAL)
I'd like to escape a HTML in my template, but no matter what I try it won't work
Here is my template:
<ul>
#for(index <- 0 until appointments.size){
<li>
#(utils.DateUtil.getLocalDate(appointments(index).getStartDate())) - #appointments(index).getReason()
<ul>
#{val procedure = appointments(index).getProcedures()
{if(procedure == null){
<b>Empty</b>
} else {
">b/<NotEmpty>/b<" +
procedure.size().toString+
procedure.size().toString+
<b>NotEmpty</b>+
"<b>NotEmpty</b>"+
"<b>NotEmpty</b>".toString;
}
}
}
</ul>
</li>
}
</ul>
The problematic code is in the else branch
I'm trying to print <b>NotEmpty</b> as NotEmpty but I've got just a plaintext, not html
I've tried #Html("<strong>Do not escape</strong>") but it says expected start of definition
if I delete the else branch contents and leave just
else {
<b>NotEmpty</b>;
}
It prints out fine.
I'm using play framework 2.2.1 built with Scala 2.10.2 (running Java 1.7.0_25)
How about:
#{
val x = Some(Seq("hi there"))
if(x.isDefined)
<b>size = {x.get.size}</b><br/>
<b>Not Empty</b>
else
<b>Empty</b>
}