I have created a custom content element without extending the tt_content columns because the existing fields in the database are sufficient for what I need.
I am using "header", "header_link" and "image" but I need the "image" column to have a different TCA configuration when it's used in my custom content element.
I can change the configuration globally:
$GLOBALS['TCA']['tt_content']['columns']['image']['config']['maxitems'] = 1;
but that's not what I want.
Something like
$GLOBALS['TCA']['tt_content']['my_custom_element']['columns']['image']['config']['maxitems'] = 1;
or
$GLOBALS['TCA']['tt_content']['columns']['my_custom_element']['image']['config']['maxitems'] = 1;
isn't working.
Does anyone know how to accomplish what I want? Thanks! :-)
It's not documented yet but the following code works in TYPO3 7.3
$GLOBALS['TCA']['tt_content']['types']['my_custom_element']['columnsOverrides'] = array(
'image' => array(
'config' => array(
'maxitems' => 1
)
)
);
#dmnkhhn is right. below is your solution if your TYPO3 CMS version is newer or equal to TYPO3 CMS 7.3
$GLOBALS['TCA']['tt_content']['types'][$myCType]['columnsOverrides']['images']['config']['maxitems'] = 1;
Note that you have to configure you new plugin type as a ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT for this to work.
Example
Take a look at the Configuration backend module TCA section and browse to (foldout) tt_content/types/html/columnsOverrides and see how the TYPO3 core handles the override of the bodytext field for the HTML content element.
The Configuration backend module is a great tool to learn and understand TCA and other global variables by seeing how other already has done the thing you want.
TCA config of columns is some kind of final, that means they are cached once and it's not possible to use different configs for one field depending on any conditions.
The typical solution is adding custom image field ie. my_image to the tt_content and replacing original image field within your CE type only
like (sample):
$GLOBALS['TCA']['tt_content']['types']['Tx_Your_Type']['showitem'] = $GLOBALS['TCA']['tt_content']['types']['image']['showitem'];
$GLOBALS['TCA']['tt_content']['types']['Tx_Your_Type']['showitem'] = str_replace(',image ,', ',my_image ,', $GLOBALS['TCA']['tt_content']['types']['Tx_Your_Type']['showitem']);
Override a field with the the configuration of another column like this:
$GLOBALS['TCA']['tt_content']['types']['myType']['columnsOverrides']['header']['config'] =
$GLOBALS['TCA']['tt_content']['columns']['header_link']['config'];
Related
Setup
I want to transfer data from my project to a TYPO3 instance. Assume I have an HTML export that generates about 20 different HTML files inside my TYPO3 directory. These files contain data from a different system and the data updates quite frequently, so I am overwriting them regularly with the newest information.
Problem
I would like to tell TYPO3 to load the HTML contents of each file as its own page. Please note: the pages are not complete html documents (no <html> or <body> tags). Instead, I want whatever code is in those files to be displayed inside the context of a TYPO3 page. Kind of like a TYPO3 HTML PageContent, but I want the source for the HTML to be from a file.
I don't care if I have to manually set up each page, but I haven't found any way to let a TYPO3 Page or PageContent get its data from a file. Do you know of any way this would be possible?
Note: iframe isn't a solution in my case. I am using TYPO3 7.6.23
My answer is based on the following assumptions:
You have you have a "frontend provider extension" EXT:yourext; if not you can change every path like EXT:yourext/Resources/Private/Etcetera with the proper ´fileadmin/etcetera/Resources/Private/Etcetera´
You use backend_layout on database to store the backend layout and use that field to control the frontend template. I don't remember if in version 7 you can also use the filesystem using key.data=pagelayout
of course you have to adjust the IDs of the backend_layout items
the files to include will be partials, stored in the folder EXT:yorext/Resources/Private/Partials/ and will be named
MyFileToIncludeOne.html
MyFileToIncludeTwo.html
et cetera
The basic TypoScript will be something like:
page.10 = FLUIDTEMPLATE
page.10{
templateName= TEXT
templateName.stdWrap {
cObject = CASE
cObject {
key.data = levelfield:-2,backend_layout_next_level,slide
key.override.field = backend_layout
//I assume you already have some templates
1 = TEXT
1.value = Default
2 = TEXT
2.value = Home
//The layouts for the "pages with html files" begin here
10 = TEXT
10.value = MyFileOne
11 =TEXT
11.value = MyFileTwo
}
}
layoutRootPaths {
0 = EXT:yourext/Resouces/Private/Layouts/Page/
}
partialRootPaths {
0 = EXT:yourext/Resouces/Private/Partials/Page/
}
templateRootPaths {
0 = EXT:yourext/Resouces/Private/Template/Page/
}
}
So, in the previous lines,
the template MyFileOne.html will include the partial MyFileToIncludeOne.html, with just writing in it:
<f:render partial="MyFileToIncludeOne"/>
You could also use distinct paths if you want to keep the files separated:
partialRootPaths {
0 = EXT:yourext/Resouces/Private/Partials/Page/
1 = fileadmin/some/other/path/
}
I hope I have not forgotten important passages. Feel free to ask for clarifications
I'm updating an old TYPO3 to latest verison 6.2.12. As I do so I replace deprecated classes with the proper namespaces. E.g. t3lib_div to \TYPO3\CMS\Core\Utility\GeneralUtility or t3lib_extmgm to \TYPO3\CMS\Core\Utility\ExtensionManagementUtility.
But I cant find the proper namespaced class for t3lib_svbase. Any suggestions?
.
And I absolutely have no clue what I need to do with the new xclassed include.
//old XCLASS
if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/foo_myfancyextension/sv1/class.tx_foomyfancyextension_sv1.php']) {
include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/foo_myfancyextension/sv1/class.tx_foomyfancyextension_sv1.php']);
}
It should look something like this:
//new XCLASS
$GLOBALS['TYPO3_CONF_VARS']['SYS']['Objects']['TYPO3\\CMS\\Frontend\\ContentObject\\FluidTemplateContentObject'] = array(
'className' => 'Enet\\FxLibrary\\Xclass\\FluidTemplateContentObject',
);
But where is the path to my extension and the class itself?
As can be found in typo3\sysext\core\Migrations\Code\LegacyClassesForIde.php t3lib_svbase has become \TYPO3\CMS\Core\Service\AbstractService.
You can't directly instantiate this class, since it is abstract, but you can work with any of it's children.
As for this block:
//old XCLASS
if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/foo_myfancyextension/sv1/class.tx_foomyfancyextension_sv1.php']) {
include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['ext/foo_myfancyextension/sv1/class.tx_foomyfancyextension_sv1.php']);
}
I assume, that this is part, which is found at the bottom of your files. You can safely remove it, since as #pgampe suggested, there is new way of XCLASSing, which no longer requires these lines.
To use the new XLASS feature, you need to provide a proper autoloading, by either sticking to the convention or by creating an ext_autoload.php file.
http://docs.typo3.org/typo3cms/CoreApiReference/ApiOverview/Autoloading/Index.html
http://docs.typo3.org/typo3cms/CoreApiReference/ApiOverview/Xclasses/Index.html
I need to make a 4 column website with Bootstrap but in typo3 the default setting is to have 2 cols max. To be able to edit each column in backend I have created a layout following this tutorial: http://blog.sebastiaandejonge.com/articles/2012/july/26/implementing-typo3s-backend-layouts/
I cannot display content of the columns on frontend though.
In the template section for a page that uses this layout I have added
agptop1 < styles.content.get
agptop1.select.where = colPos = 20
agptop2 < styles.content.get
agptop2.select.where = colPos = 21
etc which are the positions set in the layout manager.
Now I should specify the variable like agptop1 so I created a template file among the other bootstrap templates in typo3conf\ext\bootstrap_package\Resources\Private\Templates\Page
It is a copy of the default template with some things changed like...
<f:layout name="Default"/>
<f:section name="Main">
<f:cObject typoscriptObjectPath="lib.dynamicContent" data="{pageUid: '{data.uid}', colPos: '3'}"/>
<div class="container">
<div class="row">
<div class="col-sm-3">
<f:cObject typoscriptObjectPath="lib.dynamicContent" data="{pageUid: '{data.uid}', colPos: '20'}"/>
<f:format.raw>{agptop1}</f:format.raw>
</div>
...like the line
<f:format.raw>
and the colPos. I suppose this is correct? I have found this somewhere here on SO.
But when I use this as a template directly in template editor of the page I need it for I only get blank page.
I cannot find out how are the default bootstrap templates linked to the Default.html and to manu configuration and how is this all linked to Backend Layout. I need to pick a backend layout, fill in the content, then something1 must tell something2 that I want to load standart page, standart menu but a specific template with the variables defined. How can this be done?
I'm sorry if it's too basic but it's my first day in typo3 and there is no manual for the new version.
The spaces on colPos = 20 are a problem in Typoscript. Using this should help:
agptop1.select.where = colPos=20
From your answer I assume, that your are using the plugin bootstrap_package. This plugin comes with some new predefined templates and you want to add one more, right?
Step 1 adding a new backendlayout
To add another option to your backend page-options, have a look into
BackendLayouts
Each file contains the description of one backend layout and each file is built the same way:
1. Defining a title (no need of using a language file here)
2. Defining the alignment (rows cols) of the content-container in the backend
3. Defining a icon
Just copy one of these files and modify it. These files have to be included in the page-layout and bootstrap_package does it automatically when it is installed. You can turn of this behaviour in the extension setting and include all backend layouts by your own. Therefor copy the file BackendLayouts.txt from bootstrap_package to your fileadmin, add your custom new layout and include the file in you pagets-config like so:
<INCLUDE_TYPOSCRIPT: source="FILE:fileadmin/template/typoscript/BackendLayouts.txt">
Note 1: For single files use 'FILE:' for Directories use 'DIR:'
Note 2: For pagets-config go to your root-page, edit it, got to resources and scroll down. Here it is. Enter code here.
If you have done it right you could now select your new layout from backend.
Step 2 Creating your own template
Copy one of the bootstrap_package template files and modify it, by only adding new cObject elements and what you need for your alignment.
<f:cObject typoscriptObjectPath="lib.dynamicContent" data="{pageUid: '{data.uid}', colPos: '2'}"/>
In addition copy the hole dir /typo3conf/ext/bootstrap_package/Resources/Private to the fileadmin and put your own template in the subdir 'Templates'
Step 3 Bring it together
As the last step you have to modify the typoscript-setup. I recommend to copy the bootstrap setup-files and include it manually rather then import it by static template.
So remove your static template "Boostrap Package (bootstrap_package)" copy the setup.txt and constant.txt from
/typo3conf/ext/bootstrap_package/Configuration/TypoScript/
to your fileadmin dir and include it like so:
Constants:
<INCLUDE_TYPOSCRIPT: source="FILE:fileadmin/template/typoscript/constants.txt">
Setup:
<INCLUDE_TYPOSCRIPT: source="FILE:fileadmin/template/typoscript/setup.txt">
After all you have to open your custom constant.txt file and add your own paths:
page {
...
fluidtemplate {
# cat=bootstrap package: advanced/100/100; type=string; label=Layout Root Path: Path to layouts
layoutRootPath = fileadmin/template/Private/Layouts/Page/
# cat=bootstrap package: advanced/100/110; type=string; label=Partial Root Path: Path to partials
partialRootPath = fileadmin/template/Private/Partials/Page/
# cat=bootstrap package: advanced/100/120; type=string; label=Template Root Path: Path to templates
templateRootPath = fileadmin/template/Private/Templates/Page/
}
...
}
And for the last step add your new template in the config.txt into the fluidtemplate-part 'page.10.templateName ...' doing some pattern-matching =)
10 = FLUIDTEMPLATE
10 {
templateName = TEXT
templateName.stdWrap.cObject = CASE
templateName.stdWrap.cObject {
key.data = levelfield:-1, backend_layout_next_level, slide
key.override.field = backend_layout
pagets__default_clean = TEXT
pagets__default_clean.value = DefaultClean
pagets__default_2_columns = TEXT
pagets__default_2_columns.value = Default2Columns
...
I hope, that i have described everything important. If you have trouble, have deep look into bootrack_package's source code. You can learn everything by miming what they have done.
Good luck
I have a base .docx for which I need to change the page header / footer image on a case by case basis. I read that python-docx does not yet handle headers/footers but it does handle Pictures.
What I cannot work around is how to replace them.
I found the Pictures in the documents ._package.parts objects as ImagePart, I could even try to identify the image by its partname attribute.
What I could not find in any way is how to replace the image. I tried replacing the ImagePart ._blob and ._image attributes but it makes no difference after saving.
So, what would be the "good" way to replace one Image blob with another one using python-docx? (it is the only change I need to do).
Current code is:
d = Document(docx='basefile.docx')
parts = d._package
for p in parts:
if isinstance(p, docx.parts.image.ImagePart) and p.partname.find('image1.png'):
img = p
break
img._blob = open('newfile.png', 'r').read()
d.save('newfile.docx')
Thanks,
marc
There is no requirement to use python-docx. I found another Python library for messing with docx files called "paradocx" altought it seems a bit abandoned it works for what I need.
python-docx would be preferable as the project seems more healthy so a solution based on it is still desired.
Anyway, here is the paradocx based solution:
from paradocx import Document
from paradocx.headerfooter import HeaderPart
template = 'template.docx'
newimg = open('new_file.png', 'r')
doc = Document.from_file(template)
header = doc.get_parts_by_class(HeaderPart).next()
img = header.related('http://schemas.openxmlformats.org/officeDocument/2006/relationships/image')[0]
img.data = newimg.read()
newimg.close()
doc.save('prueba.docx')
I'm new to PHPUnit, and I'm having some trouble with unit testing HTML output.
My test follows:
/**
* #covers Scrap::removeTags
*
*/
public function testRemoveTags() {
// Variables
$simple_parameter = 'script';
$array_parameter = array('script', 'div');
$html = '<div class="pubanunciomrec" style="background:#FFFFFF;"><script type="text/javascript"><!-- google_ad_slot = "9853257829"; google_ad_width = 300; google_ad_height = 250; //--></script><script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"></script></div><table></table>';
// Expected HTML
$expected_html_whitout_script = new DOMDocument;
$expected_html_whitout_script->loadHTML('<div class="pubanunciomrec" style="background:#FFFFFF;"></div><table></table>');
$expected_html_without_script_div = new DOMDocument;
$expected_html_without_script_div->loadHTML('<table></table>');
// Actual HTML
$actual_whitout_script = new DOMDocument;
$actual_whitout_script->loadHTML($this->scrap->removeTags($html, $simple_parameter));
$actual_without_script_div = new DOMDocument;
$actual_without_script_div->loadHTML($this->scrap->removeTags($html, $array_parameter));
// Test
$this->assertEquals($expected_html_whitout_script, $actual_whitout_script);
$this->assertEquals($expected_html_without_script_div, $actual_without_script_div);
}
My problem is that the DOMDocument object generates some HTML code and I can't compare it. How can I print the DOMDocument object to see the output? Any clues on how to compare the HTML?
Sorry for my bad english.
Best Regards,
Since 2013, there is another way to test HTML Output using PHPUnit.
It is using assertTag() method that can be found in PHPUnit 3.7 and 3.8.
For example :
// Matcher that asserts that there is an element with an id="my_id".
$matcher = array('id' => 'my_id');
// Matcher that asserts that there is a "span" tag.
$matcher = array('tag' => 'span');
// Matcher that asserts that there is a "div", with an "ul" ancestor and a "li"
// parent (with class="enum"), and containing a "span" descendant that contains
// an element with id="my_test" and the text "Hello World".
$matcher = array(
'tag' => 'div',
'ancestor' => array('tag' => 'ul'),
'parent' => array(
'tag' => 'li',
'attributes' => array('class' => 'enum')
),
'descendant' => array(
'tag' => 'span',
'child' => array(
'id' => 'my_test',
'content' => 'Hello World'
)
)
);
// Use assertTag() to apply a $matcher to a piece of $html.
$this->assertTag($matcher, $html);
Read more in official PHPUnit Website.
You may want to consider looking at Selenium. It is a browser-based testing tool for doing functional tests for a web site.
You write scripts which involve loading a web browser and simulating clicks and other actions, and then doing asserts to check that, for example, specific page elements are present, in the correct place or contain the expected values.
The tests can be written using an IDE that runs as a plug-in for Firefox, but they can be run against all the major browsers.
We have a suite of Selenium tests that run as part of our CI process, allowing us to see very quickly if something has gone wrong with our HTML output.
All in all, its a very powerful testing tool.
Also, it integrates with PHPUnit (and other language-specific tools), so it does answer your question, although probably not in the way you were thinking of.
You should be a bit careful in comparing outputted HTML to a correct template. Your HTML will change a lot, and you can end up spending too much time on maintaining your tests.
See this post for an alternative approach.
You can use saveHtml method of DOMDocument and compare the output.
You can compare two HTML strings with PHPUnit assertXmlStringEqualsXmlString method:
$this->assertXmlStringEqualsXmlString($emailMarkup, $html);
where
$emailMarkup - expected HTML string
$html - current HTML string
Important! HTML strings must be XML-valid. For example use
<br/>
instead
<br>
Also tag attributes must have values, e.g. use
<hr noshade="true">
instead
<hr noshade>
It is best not to validate against a template (unless you want to make sure nothing changes, but that is a different condition / test that you may want). You will probably want to test that your HTML includes what the user should actually see, and not that the actual HTML that formats the output is exactly what is in a template. I would recommend sending your HTML through a converter that changes it into pure text, then testing to see if you get the right results. This accommodates future functionality and data related changes that are inevitable in software development. You don't want your tests failing because someone added a class somewhere. This is probably a custom type test you will want to code yourself to meet your needs.
It is also best to insure your HTML (and CSS) is correctly formatted, what ever it may be. Sometimes invalid HTML is parsed and displayed somewhat reasonably by the browser, but best not to rely on browsers knowing what do to with invalid HTML and CSS. I have seen many issues fixed just by correcting the HTML.
I developed a library that outputs HTML PHPFUI, and I could not find any recent or even supported HTML unit tests for PHPUnit. So I created https://packagist.org/packages/phpfui/html-unit-tester which is a modern HTML and CSS unit tester. It validates against w3.org standards, so will always be up to date with the latest.
Basically you can pass in HTML fragments, or entire pages, and it will check validity of your HTML. You can test strings, files or even live URLs. Really handy to make sure all the HTML and CSS you are generating is valid. I found so many issues with my code with this library, was definitely worth the time invested. Hope everyone can benefit from it as well.