File Import in an extension - typo3

I use to import files from another application via JSON into TYPO3. The imported files are saved in a specific storage. The associated records are created in sys_file. So far everything looks good.
Now I'd like to add the imported files to a certain table. For that I use the approach of the news extension V8.5.2 according NewsImportService.php. There is a function hydrateNewsRecord() which makes media (file) relation. Therefor I use following code:
$media = $objectManager->get(\Zhaw\ZhawContinuingEducation\Domain\Model\FileReference::class);
$media->setFileUid($file->getUid());
\\ add new file to field
$newCourse->addContactImage1($media);
...
\\ add to table course
$courseRepo->add($newCourse);
...
$persistenceManager->persistAll();
During test I always get the error (due to persistence manager): Table 'typo3_www.tx_zhawcontinuingeducation_domain_model_filereference' doesn't exist
I also included under domain/model FileReference.php and added in setup.typoscript:
objects {
TYPO3\CMS\Extbase\Domain\Model\FileReference.className = Zhaw\ZhawContinuingEducation\Domain\Model\FileReference
}
persistence {
storagePid =
classes {
Zhaw\ZhawContinuingEducation\Domain\Model\FileReference {
mapping {
tableName = sys_file_reference
columns {
uid_local.mapOnProperty = originalFileIdentifier
}
}
}
}
}
The table tx_zhawcontinuingeducation_domain_model_filereference is not necessary, because it already exists in the core. Does anybody know, what I'm missing?

Mapping model classes like that in TypoScript isn't possible anymore since TYPO3 10.0. You'll need to add a EXT:extension/Configuration/Extbase/Persistence/Classes.php file to your extension with the following content:
<?php
declare(strict_types = 1);
return [
\Zhaw\ZhawContinuingEducation\Domain\Model\FileReference::class => [
'tableName' => 'sys_file_reference',
'properties' => [
'originalFileIdentifier' => [
'fieldName' => 'uid_local'
],
],
],
];
More about this you can find here: https://docs.typo3.org/c/typo3/cms-core/master/en-us/Changelog/10.0/Breaking-87623-ReplaceConfigpersistenceclassesTyposcriptConfiguration.html

Related

TYPO3 11 call stdWrap from Extbase

How do you call the ContentObjectRenderer method stdWrap from Extbase?
The setup from a pi_base plugin is:
plugin.tx_myextension {
standard = TEXT
standard.value = Sorry, no data could be fetched!
}
In TYPO3 extensions without Extbase you have the $conf array with the keys
'standard' =>
'TEXT' (string)
and the key
'standard.' =>
'value' (array) =>
'Sorry, no data could be fetched!'.
Then you call the method by:
$out =
$cObj->stdWrap(
$conf['standard'],
$conf['standard.']
);
This will render the desired text 'Sorry, no data could be fetched!' into $out.
But in Extbase you do not have the $conf array in this way. Exbase delivers the $this->settings to store the settings part of the setup.
plugin.tx_myextension {
settings {
standard = TEXT
standard.value = Sorry, no data could be fetched!
}
}
The standard setup is internally stored by Extbase as a 2-dimensional array with this data:
'standard'
=> 'value' (Array)
=> 'Sorry, no data could be fetched!'
and
'standard'
=> '_typoScriptNodeValue' (Array)
=> 'TEXT'
I have tried it this way ($conf = $this->settings):
$out =
$cObj->stdWrap(
'',
$conf['standard']
);
And I have tried this:
$out =
$cObj->stdWrap(
$conf['standard']['_typoScriptNodeValue'],
$conf['standard']
);
But the result is an empty string only.
What is the recommended way to accomplish the stdWrap rendering with Extbase?
Basically $cObj->stdWrap only supports stdWrap instructions - however, you were using a cObject instruction (with TEXT).
Let's assume your settings look like this in TypoScript:
plugin.tx_myextension {
settings {
standard {
# cObject is a stdWrap function
# (that was the missing piece)
cObject = TEXT
cObject.value = Sorry, no data could be fetched!
}
}
}
Inside an Extbase controller $this->settings holds all data of the TypoScript path plugin.tx_myextension.settings as shown in the example before - and, the structure has been converted to be a "plain array".
Thus, inside a controller action things would look like this:
public function myAction()
{
// consider using dependency-injection instead
$typoScriptService = GeneralUtility::makeInstance(\TYPO3\CMS\Core\TypoScript\TypoScriptService::class);
// converting "plain array" back to TypoScript array
$typoScriptArray = $typoScriptService->convertPlainArrayToTypoScriptArray($this->settings['standard']);
// invoking stdWrap instruction
$cObj = $this->configurationManager->getContentObject();
$out = $cObj->stdWrap('', $typoScriptArray);
// ...
}
Additional considerations:
in Extbase, plugin settings from TypoScript and potential FlexForm settings for a specific plugin are merged
→ make sure, to keep scopes separated before "executing" it as instruction
→ otherwise editors might be able to trigger stdWrap functions via FlexForm
→ that would be an "injection" and a security vulnerability
e.g. use plugin.tx_myextension.instructions dedicated to be used in e.g. stdWrap, instead of putting everything to plugin.tx_myextension.settings
in general consider moving view-related aspects to a corresponding view or Fluid template, e.g using f:cObject view helper there
Maybe a look into the News extension can help. IMO it's a proper, flexible way without much overhead.
// Use stdWrap for given defined settings
if (isset($originalSettings['useStdWrap']) && !empty($originalSettings['useStdWrap'])) {
$typoScriptService = GeneralUtility::makeInstance(TypoScriptService::class);
$typoScriptArray = $typoScriptService->convertPlainArrayToTypoScriptArray($originalSettings);
$stdWrapProperties = GeneralUtility::trimExplode(',', $originalSettings['useStdWrap'], true);
foreach ($stdWrapProperties as $key) {
if (is_array($typoScriptArray[$key . '.'])) {
$originalSettings[$key] = $this->configurationManager->getContentObject()->stdWrap(
$typoScriptArray[$key],
$typoScriptArray[$key . '.']
);
}
}
}
// start override
if (isset($tsSettings['settings']['overrideFlexformSettingsIfEmpty'])) {
$typoScriptUtility = GeneralUtility::makeInstance(TypoScript::class);
$originalSettings = $typoScriptUtility->override($originalSettings, $tsSettings);
}
// ...
$this->settings = $originalSettings;
snippet taken from https://github.com/georgringer/news/blob/main/Classes/Controller/NewsController.php#L658-L688

Drupal, hook_form_alter add field in node edit / create

I have a node and I need to populate a field programmatically, so here is what I do :
$campaigns = $client->get_campaigns();
$tab_campaign = array(""=>"Dernière newsletter");
foreach ($campaigns->response as $camp){
$tab_campaign[$camp->CampaignID] = $camp->Name;
}
$form['field_last_newsletter'] = array(
'#type' => 'select',
'#required' => true,
'#options' => $tab_campaign,
'#title' => 'Choisir la dernière newsletter',
);
}
This work, I have my select field populated but when I select one and click on save nothing is saved, if I come back to the edit page the select have the default value, what I am doing wrong ?
Thanks.
I think you're looking for a allowed_values_function
setting for option fields. It is a perfect solution for fields with dynamic options.
First, you need to change the current field settings to use the function to set the allowed values.
To do this, modify the field settings in features (if used):
// Exported field_base: 'field_last_newsletter'
// my_module.features.field_base.inc
$field_bases['field_last_newsletter'] = array(
// ....
'settings' => array(
'allowed_values' => array(),
'allowed_values_function' => 'my_module_field_last_newsletter_allowed_values',
),
// ....
);
If you do not use features, you can make this change by executing the PHP code or using hook_update_N
/**
* Implements hook_update_N().
* Update the field_last_newsletter field settings to use callback for allowed_values.
*/
function my_module_update_N(&$sandbox) {
// get default status for field using machine name of field
$default_stats_field = field_info_field('field_last_newsletter');
// unset the allowed values
$default_stats_field['settings']['allowed_values'] = '';
// function name that provides array of values
$default_stats_field['settings']['allowed_values_function'] = 'my_module_field_last_newsletter_allowed_values';
// update value with new value.
field_update_field($default_stats_field);
}
After saving the new settings, you need to implement the callback function for the dynamic allowed values.
/**
* Allowed values callback for field_last_newsletter.
*/
function my_module_field_last_newsletter_allowed_values() {
// ...
$campaigns = $client->get_campaigns();
$tab_campaign = array(""=>"Dernière newsletter");
foreach ($campaigns->response as $camp){
$tab_campaign[$camp->CampaignID] = $camp->Name;
}
return $tab_campaign;
}
The issue here is that you are defining a field in code and since this was not created through the UI a database table to store it's value was not created. I would suggest you create this field through the UI (/admin/structure/types/manage/xxxxx/fields) and in your hook_form_alter you just change the #options array to populate it. This way a database table for your data will be created and Drupal will handle saving the data, populating the saved value, etc...

SugarCRM 6.5 CE: how to remove button in detailview according to a condition

I'm trying to remove buttons in detail view of a Lead if it is alredy converted.
I saw a similar question and it use javascript to hide buttons. I'm trying to obtain same result via php.
This is my view.detail.php in custom\modules\Leads\views\ folder
class LeadsViewDetail extends ViewDetail {
function __construct(){
parent::__construct();
}
function preDisplay() {
parent::preDisplay();
if($this->bean->converted==1) {
echo "hide";
foreach ($this->dv->defs['templateMeta']['form']['buttons'] as $key => $value) {
unset($this->dv->defs['templateMeta']['form']['buttons'][$key]);
}
} else {
echo "show";
}
}
}
Using this code, after a Quick Repair & Rebuilt, I see "hide" or "show" correctly according to the Lead status but buttons are not updated correctly.
If I open a converted Lead after QR&R, I will never see the buttons.
If I open a unconverted Lead after QR&R, I will see the buttons all times.
I'm stuck with this situation. Can anyone explain me where is the problem? How I can solve it?
Every help is very appreciated.
You can probably handle this without extending the ViewDetail by using Smarty logic ("customCode") in custom/modules/Leads/metadata/detailviewdefs.php. It looks like the Convert button is already only rendered when the user has Edit privileges, so it's not a big deal to add one more condition to it...
$viewdefs['Leads']['DetailView']['templateMeta']['form]['buttons'][] = array('customCode' => '
{if $bean->aclAccess("edit") && $bean->converted}
<input title="{$MOD.LBL_CONVERTLEAD_TITLE}"
accessKey="{$MOD.LBL_CONVERTLEAD_BUTTON_KEY}"
type="button"
class="button"
name="convert"
value="{$MOD.LBL_CONVERTLEAD}"
onClick="document.location=\'index.php?module=Leads&action=ConvertLead&record={$fields.id.value}\'" />
{/if}');
Alternatively, if you do have several conditions and they'd get too messy or difficult for Smarty logic to be reasonable, we can combine a small amount of Smarty Logic with the extended ViewDetail.
This except of custom/modules/Leads/metadata/detailviewdefs.php is actually the out-of-the-box file from SugarCRM CE 6.5.24, where it looks like they've actually tried to make this customization easier by supplying a Smarty var $DISABLE_CONVERT_ACTION. For reference, it simply needs the global config variable disable_convert_lead to be set and enabled, but I suspect that this was a relatively new feature not included in earlier versions. Still, it's a good example of using the View to set a simple Smarty variable that we can pivot on:
<?php
$viewdefs['Leads']['DetailView'] = array (
'templateMeta' => array (
'form' => array (
'buttons' => array (
'EDIT',
'DUPLICATE',
'DELETE',
array (
'customCode' => '{if $bean->aclAccess("edit") && !$DISABLE_CONVERT_ACTION}<input title="{$MOD.LBL_CONVERTLEAD_TITLE}" accessKey="{$MOD.LBL_CONVERTLEAD_BUTTON_KEY}" type="button" class="button" onClick="document.location=\'index.php?module=Leads&action=ConvertLead&record={$fields.id.value}\'" name="convert" value="{$MOD.LBL_CONVERTLEAD}">{/if}',
//Bug#51778: The custom code will be replaced with sugar_html. customCode will be deplicated.
'sugar_html' => array(
'type' => 'button',
'value' => '{$MOD.LBL_CONVERTLEAD}',
'htmlOptions' => array(
'title' => '{$MOD.LBL_CONVERTLEAD_TITLE}',
'accessKey' => '{$MOD.LBL_CONVERTLEAD_BUTTON_KEY}',
'class' => 'button',
'onClick' => 'document.location=\'index.php?module=Leads&action=ConvertLead&record={$fields.id.value}\'',
'name' => 'convert',
'id' => 'convert_lead_button',
),
'template' => '{if $bean->aclAccess("edit") && !$DISABLE_CONVERT_ACTION}[CONTENT]{/if}',
),
),
We can combine this $DISABLE_CONVERT_ACTION reference with a custom/modules/Leads/views/view.detail.php like the following to set it based on whatever condition we want:
<?php
require_once('modules/Leads/views/view.detail.php');
class CustomLeadsViewDetail extends LeadsViewDetail {
/*
* while we might normally like to call parent::display() in this method to
* best emulate what the parnts will do, we instead here copy-and-paste the
* parent methods' content because LeadsViewDetail::display() will set the
* DISABLE_CONVERT_ACTION Smarty var differently than we want.
*/
public function display(){
global $sugar_config;
// Example One: Disable Conversion when status is Converted
$disableConvert = ($this->bean->status == 'Converted');
// Example Two: Disable Conversion when there is at lead one related Call
// where the status is Held
$disableConvert = FALSE;
$this->bean->load_relationships('calls');
foreach($this->bean->calls->getBeans() as $call){
if($call->status == 'Held'){
$disableConvert = TRUE;
break; // exit foreach()
}
}
// Example Three: Disable Conversion if the User is in a specific Role, e.g.
// Interns who are great for data entry in Leads but shouldn't be making
// actual sales
global $current_user;
$disableConvert = $current_user->check_role_membership('No Lead Conversions');
// In any of the above examples, once we have $disableConvert set up
// as we want, let the Smarty template know.
$this->ss->assign("DISABLE_CONVERT_ACTION", $disableConvert);
// copied from ViewDetail::display();
if(empty($this->bean->id)) {
sugar_die($GLOBALS['app_strings']['ERROR_NO_RECORD']);
}
$this->dv->process();
echo $this->dv->display();
}
}

Customizing layout to sfWidgetFormDoctrineChoice

I am using Symfony 1.4 sfWidgetFormDoctrineChoice
I have added the checkboxes to the form, which pulls the Model data successfully. What I want to do is also include a thumbnail next to the checkbox, along with the title.
$this->setWidget('bulkUploadVideos', new sfWidgetFormDoctrineChoice(array(
'model' => 'MediaAsset',
'query' => Doctrine_Query::create()->select('u.url')->from('MediaAsset u')->orderBy('id DESC'),
'add_empty' => false,
'multiple' => true,
'expanded' => true
)
));
This does a fantastic job of pulling the query into a list of checkboxes arranged like so:
⧠ Greenjeans
⧠ Mr Magoo
⧠ Droopy
In the Media Assets table, I also have an image url that I want to include in the layout. SO it would look like this:
|-img thumbnial- | ⧠ Greenjeans
|-img thumbnail- | ⧠ Mr. Magoo
|-img thumbnial- | ⧠ Droopy
I thought maybe using a formatter class, but I do not see any change in the form.
lib/form/formatters/sfWidgetFormSchemaFormatterAllVideos.class.php
<?php
class sfWidgetFormSchemaFormatterAllVideos extends sfWidgetFormSchemaFormatter {
protected
$rowFormat = "<span class=\"my-label-class\">%label%</span>\n <span>%error%%field%%help%%hidden_fields%</span>`n",
$errorRowFormat = "<span class=\"my-error-class\" colspan=\"2\">\n%errors%</span>\n",
$helpFormat = '<br />%help%',
$decoratorFormat = "<div class='custom-video-layout'>\n %content%</div>";
}
and then i put this at the bottom of my MediaAssetsForm.class.php
public function configure() {
parent::configure();
...
..
...
$this->getWidgetSchema()->setFormFormatterName('AllVideos');
Alas, the page layout looks exactly the same. Am I incorrectly calling the Formatter, or is there a much easier way of doing this?
Which btw, still does not answer the question of how I query the image url from the table into the output for each checkbox. That's the main problem I would like to solve. Thumbnails of each record in the form.
The formatter is used to render the whole form, what you need is to change the rendering of one of the widgets.
The sfwidgetFormDoctrineChoice has an option renderer which takes a formatter as an argument. The one that you need is the sfWidgetFormSelectCheckbox. What I would do is:
create your own class which will extend the sfWidgetFormSelectCheckbox class. E.g.
class sfWidgetFormMySelectWithThumbs extends sfWidgetFormSelectCheckbox {
}
Extend the configure function so it takes another option which will hold an array of your thumbnails.
public function configure($options = array(), $arguments = array()) {
parent::configure($options, $arguments);
$this->addOption('thumbnails', array());
}
Change the formatChoices function so it adds the image in front of the checkbox (you can copy and modify the original formatChoices function).
...
$sources = $this->getOption('thumbnails');
...
$inputs[$id] = array(
'input' => sprintf('| %s | %s',
$this->renderTag('img', array('src' => $sources[$key])),
$this->renderTag('input', array_merge($baseAttributes, $attributes))
),
'label' => $this->renderContentTag('label', self::escapeOnce($option), array('for' => $id)),
);
Use the formatter class in your widget:
$this->setWidget('bulkUploadVideos', new sfWidgetFormDoctrineChoice(array(
...
'renderer' => new sfWidgetFormMySelectWithThumbs(array('thumbnails' => $thumbanils))
)
));
Of course you need to retrieve the list of thumbnails as an array where the array keys are the same as the id's used for values for the checkboxes, but that shouldn't be an issue.

SugarCRM Adding Additional Details Info icon in Detail View

in SugarCRM some modules like "Calls" has an "i" (Additional Details) icon in List view which shows some additional details about that record.
I want to display same kind for other modules like customer visits with some custom details of the records.
Any hints or guidance will be helpful.
1) Create a file in your metadata folder {MODULENAME}/metadata/additionalDetails.php. You have to find correct place of your module.
custom/modules/MODULENAME/metadata/
custom/modulebuilder/packages/PACKAGENAME/modules/MODULENAME/metadata/
etc...
2) and create a function something like this. replace {MODULENAME} and {MODULE_BEAN_NAME} with actual module name in all places.
function additionalDetails{MODULE_BEAN_NAME}($fields) {
static $mod_strings;
if(empty($mod_strings)) {
global $current_language;
$mod_strings = return_module_language($current_language, '{MODULENAME}');
}
$overlib_string = '';
if(!empty($fields['NAME']))
$overlib_string .= '<b>'. $mod_strings['LBL_NAME'] . '</b> ' . $fields['NAME'] . ' <br>';
//Add whatever info you want to show up to $overlib_string
$editLink = "index.php?action=EditView&module={MODULENAME}&record={$fields['ID']}";
$viewLink = "index.php?action=DetailView&module={MODULENAME}&record={$fields['ID']}";
return array(
'fieldToAddTo' => 'NAME',
'string' => $overlib_string,
'editLink' => $editLink,
'viewLink' => $viewLink
);
}
you have to create $overlib_string with your data (in html). If you need edit and view links on your modal box you have to return them as well. $fields is an associative array that contains db record.
3) The i icon should appear on the module list view.