Upgrade CI3 to CI4 - configuration files - codeigniter-3

In my Codeigniter 3, I have a simple settings.php file that looks something like this:
<?php
$config["lang1_HTML"] = "sr-Latn-RS";
$config["lang1_HTML_2"] = "sr-Latn";
$config["lang1_HTML_3"] = "";
$config["lang1_code"] = "SRP";
$config["lang1_flag"] = "/images/flags/rs.png";
$config["sr"] = "lang1";
$config["lang3"] = "en";
$config["lang3_HTML"] = "en-RS";
$config["lang3_HTML_2"] = "en";
$config["lang3_HTML_3"] = "";
$config["lang3_code"] = "ENG";
...
Now I want to upgrade this to CI4. Is there any chance to put this file in app\Config without changing it and still be able to access this array?
Or better is it possible to autoload Settings.php and use it like this?

Upgrading from 3.x to 4.x » Upgrade Configuration
Upgrade Guide
You have to change the values in the default CI4 config files according to the changes in the CI3 files. The config names are pretty
much the same as in CI3.
If you are using custom config files in your CI3 project you have to create those files as new PHP classes in your CI4 project in
app/Config. These classes should be in the Config namespace and should extend CodeIgniter\Config\BaseConfig.
Once you have created all custom config classes, you have to copy the variables from the CI3 config into the new CI4 config class as
public class properties.
Now, you have to change the config fetching syntax everywhere you fetch config values. The CI3 syntax is something like
$this->config->item('item_name');. You have to change this into
config('MyConfigFile')->item_name;.
Step A:
Create a new class app/Config/Settings.php in the Config namespace that extends CodeIgniter\Config\BaseConfig.
Step B:
Copy the variables from the CI3 config into the new CI4 config class as public class properties. I.e:
<?php
namespace Config;
class Settings extends \CodeIgniter\Config\BaseConfig
{
public string $lang1_HTML = "sr-Latn-RS";
public string $lang1_HTML_2 = "sr-Latn";
public string $lang1_HTML_3 = "";
public string $lang1_code = "SRP";
public string $lang1_flag = "/images/flags/rs.png";
public string $sr = "lang1";
// ...
}
Step C:
Change the config fetching syntax everywhere you fetch config values.
I.e: from $this->config->item('lang1_HTML'); to config(\Config\Settings::class)->lang1_HTML;.
Summary:
CodeIgniter 3.x
CodeIgniter 4.x
1. Loading custom config files.
Manual Loading $this->config->load('config_filename');
CodeIgniter 4.x will automatically look for the files in all defined namespaces as well as /app/Config/.
2. Dealing with name collisions.
If you need to load multiple config files, normally they will be merged into one master $config array. To avoid collisions you can set the second parameter to TRUE and each config file will be stored in an array index corresponding to the name of the config file. Load config file: $this->config->load('settings', TRUE); Access item: $this->config->item('settings')['lang1_HTML']
You don't have to worry about this since all config files reside in their own individual classes. Access item: config(\Config\Settings::class)->lang1_HTML
3. Fetching Config items.
$this->config->item('lang1_HTML');
config(\Config\Settings::class)->lang1_HTML
4. Dynamically setting a config item or changing an existing one.
Set item: $this->config->set_item('lang1_HTML', 'sr-Cyrl-ME'); Access set item: $this->config->item('lang1_HTML');
Set item: config(\Config\Settings::class)->lang1_HTML = 'sr-Cyrl-ME' Access set item: config(\Config\Settings::class)->lang1_HTML
5.Auto-loading.
Global Auto-loading: Open the autoload.php file, located at application/config/autoload.php, and add your config file as indicated in the file. $autoload['config'] = array('settings');
Global Auto-loading: There is no need to configure this since the config(...) helper function returns a shared instance of the particular config class by default. Hence, you can always access your configs using: config('config_class_here')->item_name_here;

first go to app config
public $defaultLocale = 'en';
/**
* --------------------------------------------------------------------------
* Negotiate Locale
* --------------------------------------------------------------------------
*
* If true, the current Request object will automatically determine the
* language to use based on the value of the Accept-Language header.
*
* If false, no automatic detection will be performed.
*
* #var bool
*/
public $negotiateLocale = true;
/**
* --------------------------------------------------------------------------
* Supported Locales
* --------------------------------------------------------------------------
*
* If $negotiateLocale is true, this array lists the locales supported
* by the application in descending order of priority. If no match is
* found, the first locale will be used.
*
* #var string[]
*/
public $supportedLocales = ['en','fa'];
go to app create folder language
folder en and folder fa
<?php
en/trans.php
return [
'auth' => [
'validation' => 'information is not valid',
'loggedIn' => 'you are already login',
'notLogIn' => 'you are not login',]];?>
fa/trans.php
<?php
return [
'auth' => [
'validation' => 'خطای اطلاعات وارد شده',
'loggedIn' => 'شما قبل وارد شده بودید',
'notLogIn' => 'شما وارد نشدید',]];?>
read header it send to ci4 --
Accept-Language en
or Accept-Language fa
echo lang('trans.auth.validation');

Related

How to access the ext_conf_template.txt (extension configuration) in typoscript?

There are a few settings in the ext_conf_template.txt in my extension.
I want to check the value of one of these settings, but in typoscript, not in PHP.
In PHP it works like this:
unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['myExt'])
How should I do this in typoscript?
Thanks to the answer of Marcus I was able to get a extension configuration setting into typoscript. First create the extension setting in ext_conf_template.txt:
# cat=Storage; type=string; label=storageFolderPid
storageFolderPid = 1
In ext_local_conf.php add the following lines:
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTypoScriptConstants(
"plugin.tx_extensionname.settings.storageFolderPid = ".$GLOBALS['TYPO3_CONF_VARS']['EXTENSIONS']['extension']['storageFolderPid']
);
Then you can use this variabele in your typosript, for instance to create a submenu of a storage folder:
lib.submenu = CONTENT
lib.submenu {
table = tx_extension_domain_model_article
select {
pidInList = {$plugin.tx_extensionname.settings.storageFolderPid}
selectFields = tx_extensionname_domain_model_article.*
}
...
}
I did something similar in my code snippet extension (see complete code on Github), where I just added a custom TypoScript condition:
[DanielGoerz\FsCodeSnippet\Configuration\TypoScript\ConditionMatching\AllLanguagesCondition]
// some conditional TS
[global]
The condition implementation is quite simple:
namespace DanielGoerz\FsCodeSnippet\Configuration\TypoScript\ConditionMatching;
use DanielGoerz\FsCodeSnippet\Utility\FsCodeSnippetConfigurationUtility;
use TYPO3\CMS\Core\Configuration\TypoScript\ConditionMatching\AbstractCondition;
class AllLanguagesCondition extends AbstractCondition
{
/**
* Check whether allLanguages is enabled
* #param array $conditionParameters
* #return bool
*/
public function matchCondition(array $conditionParameters)
{
return FsCodeSnippetConfigurationUtility::isAllLanguagesEnabled();
}
}
And the check for the actual TYPO3_CONF_VARS value is done in FsCodeSnippetConfigurationUtility:
namespace DanielGoerz\FsCodeSnippet\Utility;
class FsCodeSnippetConfigurationUtility
{
/**
* #return array
*/
private static function getExtensionConfiguration()
{
return unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['fs_code_snippet']);
}
/**
* #return bool
*/
public static function isAllLanguagesEnabled()
{
$conf = self::getExtensionConfiguration();
return !empty($conf['enableAllLanguages']);
}
}
Maybe that fits your needs.
Handle the configuration via Extension Manager and call ExtensionManagementUtility::addTypoScriptConstants() in your ext_localconf.php to set a TypoScript constant at runtime.
This way the value can be set at one location and is available both in lowlevel PHP and TypoScript setup.

Is there a way to create objects/ table entries during the extension installation in TYPO3?

I want to create objects during the installation of an extension. For example I have the following two simple domain models:
class Product extends AbstractEntity
{
protected $name = '';
protected $sku = '';
...
}
class Location extends AbstractEntity
{
protected $name = '';
protected $city = '';
...
}
and a third domain model like:
class Mapper extends AbstractEntity
{
protected $domainModelName = '';
protected $domainModelProperty = '';
}
Now I want too add entries like this:
domain_model_name | domain_model_property
Product | name
Product | sku
Location | city
....
during the extension installation or directly after the installation, so that the tx_foxexample_domain_model_mapper table will be filled automatically, is this possible?
I know that I can use a initializeAction, but then the entries will only be generated if I add a plugin and visit the page etc., but I want that the entries/ objects already exists before I use a plugin or add some objects.
You can store your static data in the file ext_tables_static+adt.sql which must be located in the root folder of your extension.
According to the TYPO3 API, you must should use the following command to export your static data
mysqldump --password=[password] [database name] [tablename] --add-drop-table > ./ext_tables_static.sql
Also make sure, that the table structure of static tables is present in the ext_tables.sql file.
The extension static_info_tables makes use of this technique. You can have a look at the extension here for more details.

Delete file when deleting sys_file_reference

I am writing an extension which allows to upload files in the frontend and backend of a TYPO3 instance. The upload works in both views but if the admin wants to delete an upload in the backend in list view, the "physical" file, which is located on the harddisk of the webserver, will not be deleted, only the sys_file_reference record.
Is there a possibility to tell the tca that in case of a deletion of the upload record the associated file should also be deleted? I've also tried to implement a slot with the following code but nothing happens:
ext_localconf.php:
\TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\SignalSlot\\Dispatcher')->connect(
'TYPO3\CMS\Extbase\Persistence\Generic\Backend',
'afterRemoveObject',
'Kmi\feupload\Slots\MyAfterRemoveObjectSlot',
'myAfterRemoveObjectMethod'
);
Classes/Slots/MyAfterRemoveObjectSlot.php:
namespace Kmi\feupload\Slots;
class MyAfterRemoveObjectSlot {
public function myAfterRemoveObjectMethod($object) {
// do something
\TYPO3\CMS\Extbase\Utility\DebuggerUtility::var_dump($object);
}
}
Has anyone an idea how to solve this? There will be many uploads and if the admin deletes one, the associated file should also be deleted...
Thank you in advance for your help :)
Unfortunately I don't have time to create a complete, tested answer ATM but I'm putting together the steps needed and hope that you can work a solution and complete my answer then.
Every manipulation done through a TCEFORM is saved with the DataHandler (formerly called TCEmain). The DataHandler has numerous hooks. I assume that your model "Upload" has a property file which is of type (or extends) \TYPO3\CMS\Extbase\Domain\Model\FileReference.
File references in TCEFORM are added as IRRE elements. So when you remove the file reference and save the Upload object, the following data is (amogst others) sent to DataHandler:
cmd[sys_file_reference][15011][delete]=1
This means that the file reference with uid 15011 must be deleted. I suggest to implement the processCmdmap_deleteAction hook for this.
So you must also check the datamap to find out if the command was executed through a manipulation of an "Upload" record.
ext_localconf.php:
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass']['your_extension'] = 'My\\Extension\\Hook\\DataHandler';
EXT:your_extension/Classes/Hook/DataHandler.php
This code is untested!
<?php
namespace My\Extension\Hook
class DataHandler {
/**
* #param string $table
* #param int $id
* #param array $recordToDelete
* #param $parentObject \TYPO3\CMS\Core\DataHandling\DataHandler
*/
public function processCmdmap_deleteAction($table, $id, $recordToDelete, $parentObject) {
if (array_key_exists('tx_myext_domain_model_upload', $parentObject->datamap)) {
// Parent record of record to delete is of type "tx_myext_domain_model_upload"
if ($table === 'sys_file_reference' && is_integer($id)) {
// A file reference was requested to delete
// Get an instance of the ResourceFactory
/** #var $resourceFactory \TYPO3\CMS\Core\Resource\ResourceFactory */
$resourceFactory = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\ResourceFactory');
// We get the FileReference object for the given id
$fileReferenceObject = $resourceFactory->getFileReferenceObject($id);
// Delete the original file of the file reference
$fileWasDeleted = $fileReferenceObject->getOriginalFile()->delete();
// #TODO throw a warning if $fileWasDeleted is false
}
}
}
}
I commented the code so you know which checks are necessary for what.
Don't forget to clear the system cache after defining the hook in ext_localconf.php.
// delete video or image from sys_file table and sys_file_reference
// table (here videourl - sys_file_reference fieldname)
$fileRepository = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\FileRepository::class);
$fileObjects = $fileRepository->findByRelation('tablename', 'videourl', $id);
foreach ($fileObjects as $fileKey => $fileValue) {
$delete= $fileValue->getOriginalFile()->delete();
}

TYPO3 Extbase individual code on backend-deletion of an object

I would like to execute some individual code when one of my Extbase domain objects is deleted from the list view in TYPO3 backend.
Thought that it could / would work by overwriting the remove( $o ) method in the according repository like
public function remove( $object ) {
parent::__remove( $object );
do_something_i_want();
}
, but that won't work I guess. Looks like the repository-methods are only called / used by actions of my extension (e.g. if I had some delete-action in a FE- or BE-plugin) but not when the object is just deleted from list view in the backend? I don't use (up to now) any FE/BE-plugin / -actions - only the simple add/edit/delete functions in the backends list view of my storage folder.
Background: I have e.g. two models with some 1:n relation (let's say singer and song), where one object includes some uploaded file (album_cover > pointing to the file's URL in /uploads/myext/ folder); using #cascade works fine for deleting every song belonging to a singer that is deleted, but it won't touch the file uploaded (only) for song.album_cover - leading to quite some waste over time. So I would love to do some sort of onDeletionOfSinger() { deleteAllFilesForHisSongs(); }-thing.
(Same thing would apply on deletion of let's say a single song and it's album_cover-file.)
Sounds quite easy & common, but I just don't get behind it and found nothing useful - would love some hint / pointing to the right direction :-).
List view uses TCEmain hooks during its operations, so you can use one of them to intersect delete action, i.e.: processCmdmap_deleteAction
Register your hooks class in typo3conf/ext/your_ext/ext_tables.php
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass'][] = 'VENDORNAME\\YourExt\\Hooks\\ProcessCmdmap';
Create a class with valid namespace and path (according to previous step)
file: typo3conf/ext/your_ext/Classes/Hooks/ProcessCmdmap.php
<?php
namespace VENDORNAME\YourExt\Hooks;
class ProcessCmdmap {
/**
* hook that is called when an element shall get deleted
*
* #param string $table the table of the record
* #param integer $id the ID of the record
* #param array $record The accordant database record
* #param boolean $recordWasDeleted can be set so that other hooks or
* #param DataHandler $tcemainObj reference to the main tcemain object
* #return void
*/
function processCmdmap_postProcess($command, $table, $id, $value, $dataHandler) {
if ($command == 'delete' && $table == 'tx_yourext_domain_model_something') {
// Perform something before real delete
// You don't need to delete the record here it will be deleted by CMD after the hook
}
}
}
Don't forget to clear system cache after registering new hook's class
In addition to biesiors answer I want to point out, that there is also a signalSlot for this. So you can rather register on that signal than hooking into tcemain.
in your ext_localconf.php put:
$signalSlotDispatcher =
\TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\SignalSlot\\Dispatcher');
$signalSlotDispatcher->connect(
'TYPO3\CMS\Extbase\Persistence\Generic\Backend',
'afterRemoveObject',
'Vendor\MxExtension\Slots\MyAfterRemoveObjectSlot',
'myAfterRemoveObjectMethod'
);
So in your Slot you have this PHP file:
namespace Vendor\MxExtension\Slots;
class MyAfterRemoveObjectSlot {
public function myAfterRemoveObjectMethod($object) {
// do something
}
}
Note thet $object will be the $object that was just removed from the DB.
For more information, see https://usetypo3.com/signals-and-hooks-in-typo3.html

How can I selectively apply a VSTemplate?

I am creating a custom VSTemplate for MVC 4 applications for my company that uses a wizard that is comparable to the wizard that appears when you create a new MVC4 application. I have one of two templates I would like to apply when the developer creates a new app of this type as shown here:
Both of those entries correspond to templates that are defined inside my VSIX project under a folder called ProjectTemplates:
My question is, how do I apply the correct template when the wizard runs? I know how to create a vstemplate with multiple projects (using the ProjectCollection node in the vstemplate), but that's not really what I want to do since they will never be deployed together. I see that I can add both vstemplates as Assets to my vsixmanifest file, but I'm not really sure how to apply just one template conditionally.
Thanks!
You'll need to include the files for your "optional" template(s) in sub directories of the the "root" template folder but EXCLUDE them from the TemplateContent Element of the "root" template.
Your IWizard implementation needs to keep a reference to the EnvDTE.DTE object (the first parameter of RunStarted) and use it in the ProjectFinishedGenerating to add the projects to the solution using the template(s) that match what the user selected.
public class SelectTemplatesWizard : IWizard
{
private EnvDTE.DTE _dte = null;
private string _solutionDir = null;
private string _templateDir = null;
public void RunStarted(object automationObject, Dictionary<string, string> replacementsDictionary, WizardRunKind runKind, object[] customParams)
{
// Store the reference to the environment for later use
_dte = automationObject as EnvDTE.DTE;
/*
The value of the item in the replacements dictionary for the key
$destinationdirectory$ is populated with the physical location of
the directory (named based on the user entered project name) created
sibling to the solution file.
The solution directory will be this directories parent
when the Type attribute of the VSTemplate element is ProjectGroup
*/
_solutionDir = System.IO.Path.GetDirectoryName(replacementsDictionary["$destinationdirectory$"]);
// customParams[0] is a default custom param that contains the physical location of the template that is currently being applied
_templateDir = System.IO.Path.GetDirectoryName(customParams[0] as string);
}
public void ProjectFinishedGenerating(Project project)
{
int userSelected = 1;
string name= null, projectPath= null, templatePath = null;
switch (userSelected)
{
case 0:
{
name = "Angular";
projectPath = System.IO.Path.Combine(_solutionDir, "Angular");
templatePath = System.IO.Path.Combine(_templateDir , "Angular\Angular.vstemplate");
}
break;
case 1:
{
name = "MVC4";
projectPath = System.IO.Path.Combine(_solutionDir, "MVC4");
templatePath = System.IO.Path.Combine(_templateDir , "MVC4\MVC4.vstemplate");
}
break;
}
_dte.Solution.AddFromTemplate(templatePath, projectPath, name);
}
/* Other IWizard methods excluded for brevity */
}