SharePoint - Set custom publishing page layout custom field programatically - dom

I have a custom publishing page content type, based on the Publishing Article Page content type. On this content type, I have a custom field named "PageContentCategory". In my code to create new pages, I tried this:
PublishingPage newPublishingPage = this.currentPublishingWeb.GetPublishingPages().Add(pageName, newPageSelectedLayout);
if (pageContent.IsEmpty())
{
pageContent = Properties.Resources.EAWorldArticleHandler_CreateNewArticlePage_DefaultPageContent;
}
newPublishingPage.ListItem[new Guid("{93496B35-7EC3-4132-B0D0-3BDC5606F5EF}")] = pageContentCategory;
newPublishingPage.ListItem[FieldId.PublishingPageContent] = pageContent;
newPublishingPage.Title = pageTitle;
newPublishingPage.Update();
I have also tried to set it by the field name:
PublishingPage newPublishingPage = this.currentPublishingWeb.GetPublishingPages().Add(pageName, newPageSelectedLayout);
if (pageContent.IsEmpty())
{
pageContent = Properties.Resources.EAWorldArticleHandler_CreateNewArticlePage_DefaultPageContent;
}
newPublishingPage.ListItem["PageContentCategory"] = pageContentCategory;
newPublishingPage.ListItem[FieldId.PublishingPageContent] = pageContent;
newPublishingPage.Title = pageTitle;
newPublishingPage.Update();
Both of these methods throw an error. Is there any way for me to set my custom field's value in code like this?

Try calling the Update method on newPublishingPage.Listitem not on newPublishingPage itself.
Like this:
newPublishingPage.ListItem["PageContentCategory"] = pageContentCategory;
newPublishingPage.ListItem.Update();
and then you maybe also need some of these lines, depending in the configuration of your page library
newPublishingPage.Checkin();
newPublishingPage.Publish();
newPublishingPage.Approve();

So, the solution to my problem was that I had to programmatically add the content type to the pages list instead of letting it be added automatically the first time a page with that content type was added. Apparently if you let SharePoint automatically add the content type to the pages list then it somehow doesn't get bound properly. So adding the content type first solved my problem.

Related

Retrieve content element field from within a plugin template?

I am modifying the template of a plugin, and I want to retrieve a field from the content element.
Using f:debug I see the only data available is from the plugin itself, and none from the content element.
Is there any way I can perhaps insert the field I need in the plugin settings?
eg. something like:
plugin.tx_plugin.settings {
contentUid = TEXT
contentUid.field = uid
}
The best way I can think of to do this is with a custom ViewHelper. Something like:
namespace MyVendor\MyExtension\ViewHelpers;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManager;
use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
class ContentUidViewHelper extends AbstractViewHelper
{
public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext)
{
$configurationManager = GeneralUtility::makeInstance(ConfigurationManager::class);
return $configurationManager->getContentObject()->data['uid'];
}
}
In your Fluid template:
<mynamespace:contentUid />
This will get the uid of the content element, but you can get any field this way. Just change the key of the data array to the field you need.
In the corresponding method (like the listAction or showAction) of the controller you can get the data of the content element in the following way:
$contentObject = $this->configurationManager->getContentObject();
$this->view->assign('contentObjectData', $contentObject->data);
As far as I know, you can't get to that data using typoscript, but I've never needed it anyway since I've been using the above code in the controller.
settings do not have stdWrap-type per default, but only string. So you can not use cObjects as values.
For one (or a few) settings, you could do the stdWrap-processing in your method/class yourself:
$settingsAsTypoScriptArray = $this->objectManager->get(TypoScriptService::class)->convertPlainArrayToTypoScriptArray($this->settings);
$contentObj = $this->configurationManager->getContentObject();
if ($contentObj === null) {
$contentObj = GeneralUtility::makeInstance(ContentObjectRenderer::class);
}
// now override the values in the settings array with the processed value
$contentUid = (int)$contentObj->stdWrap($settingsAsTypoScriptArray['contentUid'], $settingsAsTypoScriptArray['contentUid.']);
If you wanna have many settings to be stdWraped, have a look into EXT:news. Georg implemented an interesting concept via useStdWrap configuration.

Can't get changed property from attachPropertyChange

I would like to know which property in the JSON model has changed when modified by a view.
For a test I took OpenUI5 walkthrough example and added the following lines in the application controller
oProductModel.attachPropertyChange( function(oEvent){
console.log("event: ", oEvent);
}, this);
When I change a property in the text input, the function in the attachPropertyChange is called but oEvent object is empty as I print it in console.
I know I could connect to text input change event, but I would like to use attachPropertyChange in case there would be multiple views of the same model.
As far as I understood, you'd like to avoid using the change event of the Input control because there is no information about which property in the model has changed. However, you can still get all the relevant information within the change handler via:
oControl.getBinding(/*controlPropertyName*/).getPath() to get the name of the bound property, or
oControl.getBindingContext(/*modelName*/).getPath(/*suffix*/) to get the path of the bound context. The getPath here awaits an optional suffix that will be appended to the context path with a "/" in between.
Combine those two APIs to get an absolute path in case the property binding was relative. E.g.:
onInputChange: function (event) {
const inputControl = event.getSource();
const property = inputControl.getBinding("value").getPath(); // "myProperty"
const absolutePath = inputControl.getBindingContext(/*modelName*/).getPath(property) // "/0/myProperty"
// ...
},
You can use change event for all input field in UI, and write event handling method in the controller. You will get the property as well as value in the oEvent of the event handling method easily. I hope you understood.

Extbase mapOnProperty with inline field

I have an extension with an extbase model, which I want to extend. Works fine for all fields, except an "inline" field. When I map that inline field, I do not receive all child elements, but only one child element with the counter as uid.
Example: The inline field "description" has three children (uid = 17, uid = 18, uid = 19), so the field tx_firstextension_domain_model_job.description contains the number 3. In the frontend, I have an ObjectStorage, containing exactly one Description model (uid = 3)
Here is my TypoScript:
config.tx_extbase {
objects.Foo\FirstExtension\Domain\Model\Job.className = Bar\SecondExtension\Domain\Model\Job
persistence.classes.Bar\SecondExtension\Domain\Model\Job.mapping {
table = tx_firstextension_domain_model_job
columns {
description.mapOnProperty = description
anyotherfield.mapOnProperty = anyotherfield
onemorefield.mapOnProperty = onemorefield
}
}
}
SOLUTION
My problem was a wrong configuration option. Correct is tableName, but I used table. Here is the corrected - end even more simple - snippet:
config.tx_extbase {
objects.Foo\FirstExtension\Domain\Model\Job.className = Bar\SecondExtension\Domain\Model\Job
persistence.classes.Bar\SecondExtension\Domain\Model\Job.mapping {
tableName = tx_firstextension_domain_model_job
}
}
There seems to be a misconfiguration in your Job-Models configuration. Please check the following:
Is inside the model (Job.php) the property description configured as ObjectStorage (including getter and setter function + annotations correctly) and does the property be instantiated as ObjectStorage in the __construct function?
Is the field correctly configured as type "inline" in the TCA configuration file Configuration/TCA/tx_secondextension_domain_model_job.php
Probably you have to extend the TCA of the first extension in order to make inline work when extending other extensions. Then you maybe have to add the field description to Configuration/TCA/Overrides/tx_firstextension_domain_model_job.php but I'm not sure if this is really necessary...

How to fetch value from custom multifield component?

I have created a multifield custom widget having two fields with names ./urlLink and ./urlText.
Now i m trying to fetch the values from widget into the component's jsp with following code
String property = properties.get("./urlLink",String[].class);
for(String value: property ) {
out.print(value);
}
out.print(property);
But i am not able to get its value instead i m getting error.
If you're getting a property and it contains a string value, you need to use the method getString() - that way when you have the property, you can set the string to the value by doing something like this:
Property property = properties.get("./urlLink",String.class);
String value = property.getString();
Just a side note, if your return is supposed to be a string array, your type that you're putting the values in should be a string array.
String[] value
Check out the documentation on day.com for Properties and getting the values inside them.
Looks like a typo: you don't prefix a property name with .\ when accessing it.
My guess is you got a NullPointerException, right? That's because there's no ./urlLink property in the value map (properties). You should check against that anyway (so that it's not thrown on a fresh page with no content).
If that doesn't help -- double check that you have the properties in the content (call your page with .xml or .infinite.json extensions, and then double check if you can read them as plain strings (you should be able to -- CRX does some magic, smart type conversions).
It's good to register custom xtype as :
// registering the custom widget with the name dualfield
CQ.Ext.reg("dualfield", CQ.Ext.form.DualField);
Then u can easily fetch the value as :
String[] data = properties.get("multi",String[].class);
Here multi is the name of widget having multifield as xtype

TYPO3 Extension: Generate a PDF

Im trying to make an extension with Kickstarter that overrides the normal rendering of the page, and renders a PDF file. For this im using FPDF. But im not sure how to do it. I tried doing this, but it didnt work:
<?php
// require_once(PATH_tslib . 'class.tslib_pibase.php');
class tx_ishurkunde_pi1 extends tslib_pibase {
public $prefixId = 'tx_ishurkunde_pi1';
public $scriptRelPath = 'pi1/class.tx_ishurkunde_pi1.php';
public $extKey = 'ish_urkunde';
public $pi_checkCHash = TRUE;
public function main($content, array $conf) {
if (!t3lib_extMgm::isLoaded('fpdf')) return "Error!";
$pdf = new FPDF();
$pdf->AddPage();
$content = $pdf->Output('', 'S');
return $content;
}
}
?>
It still keeps rendering the normal web template. What am I missing?
FYI, Im not trying to render the HTML as PDF. Im trying to generate a PDF from scratch, using the URL parameters are text variables.
As far as I understood, your aim is to render a PDF instead of page elements.
Your current approach will not work since you are inserting a plugin onto the page. The plugin's return value is then given back to the TYPO3 content parser, and if the page has finished parsing, it is displayed. There is no part in it where you can throw over the whole page rendering; At least it is not intended to do, and you shouldn't to (albeit there are extensions that do this).
The eID approach would be to either create an eID script (have a look at dd_googlesitemap) which is called via GET param and renders only what you need. There you basically can do everything you want to.
In your extension's ext_localconf.php you register the eID script, like this:
$GLOBALS['TYPO3_CONF_VARS']['FE']['eID_include'][$_EXTKEY] = "EXT:{$_EXTKEY}/path/to/my/EIDHandler.php";
An Example eID handler structure:
class Tx_MyExt_EIDHandler
{
public function main()
{
// Your code here
}
}
$output = t3lib_div::makeInstance('Tx_MyExt_EIDHandler');
$output->main();
To call your eID script in the frontend, you append the appropriate GET params, like http://example.com/index.php?eID=tx_myext. This is the array key you defined in your ext_localconf.php (in my example, it is $_EXTKEY, but it can basically be any string).
The plugin/typoscript approach would be like e.g. TemplaVoila does it: You create a PAGE type and call a user_func which does your things. This would be the fastest approach because you already have a plugin. Important is that you render your own page type with only your plugin in it.
Example TypoScript:
specialPage = PAGE
specialPage {
type = 2
10 = USER
10.userFunc = tx_myextension_pi1->myFunc
}
After that, you can call your new page with http://example.com/index.php?type=2. However, headers etc are still rendered and you may need to remove them.