On SS 4.0.3 following this guide and the official doc, I successfully created a custom permission role.
Now I would create a custom group and add the default admin to it, as action performed by default in order to maintain those user/group/role settings either if the DB being dropped. I googled around many times but I didn't found any detailed tutorial to achieve this (using the Group class, the right place to implement this logic and so on).
Could anyone show me the way?
Thanks in advance.
If you want to enforce a data structure you can use DataObject::requireDefaultRecords. See Group::requireDefaultRecords for an example of this, it runs on dev/build every time.
You will want to check whether the data you're creating exists before doing so though, to ensure you don't create the group every time.
I successfully setted up the panorama described on my question, thanks to #robbie and other sources that I combined all in one, in order to achieve my goal (I'll list them here below). I wanna share my approach to those could be face this logic in the future.
In the first place I created a brand new standard/global permission with providePermissions() in the PageController (see balbuss.com source).
Next, as #robbie suggested, I created a Group DataExtension in order to build up a new group and setted my previously created permission on it (see #Barry's solution source).
To add the default admin to this group, I had to create a new Permission DataExtension in which I assigned the group to the proper member (see #StefGuev suggestion source):
// Definizione Namespace
use SilverStripe\ORM\DataExtension;
use SilverStripe\Security\DefaultAdminService;
use SilverStripe\Security\Member;
use SilverStripe\Security\Group;
class PermessoExtension extends DataExtension
{
/**
* Metodo gestione inizializzazione records di default
* Setter
* #return void
*/
public function requireDefaultRecords()
{
parent::requireDefaultRecords();
$userAdmin = DefaultAdminService::getDefaultAdminUsername();
$admin = Member::get()->filter('Email', $userAdmin)->first();
$gruppo = Group::get()->filter('Code', 'negozianti')->first();
// Controllo gruppo
if (!$admin->inGroup($gruppo->ID)) {
$admin->Groups()->add($gruppo);
$admin->write();
}
}
}
I made this choice because during dev/build, Permission being built after Member table, so I could get the default admin without errors.
Special thanks to these sources:
balbuss.com
Bereusei's solution
Barry's solution
StefGuev suggestion
Related
Working on Typo3 11.5.13
I'm trying to update some data on my pages table after a be_user changed something.
I read something about setting hooks for that purpose but I can't seem to find a good explanation as to how hooks actually function within Typo3 and how to configure one, especially for my purpose.
As far as I can see, this problem I have should be quickly solved but the complexity of the typo3 doc is hindering my progress again. Maybe you can explain how I can accomplish my goal.
Simply put: A backend user is supposed to choose a date in a datepicker and some dateinterval in the settings of a page. After saving(Or even after picking both values) I would like to update the "Next time happening" field the user can see but not change to be updated to the given date plus the dateinterval chosen.
If you have some sort of idea please share it with me.
Generally hooks are not that good documented. Modern Events are easier to find and better commented. However, if I get your use case right, using DataHandler Hooks are they way to go. That mean, every place which are using the DataHandler to save data are then covered. The backend form engine are using DataHandler.
Basic information about hooks in the core documentation:
https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/Events/Hooks/Index.html
How to identify or find hooks, events, signalslots (depending on TYPO3 version):
https://usetypo3.com/signals-and-hooks-in-typo3.html
https://daniel-siepmann.de/posts/migrated/how-to-find-hooks-in-typo3.html
Introduction or "DataHandler" explained:
https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/Typo3CoreEngine/Database/Index.html
Basicly, DataHandler has two main kind of processings:
Data manipulations -> process_datamap()
Actions (move,delete, copy, translate) -> process_cmdmap()
For DataHandler, you register a class only for datamap and/or processmap, not for a concrete hook itself.
// <your-ext>/Classes/Hooks/MyDataHandlerHooks.php
namespace <Vendor>\<YourExt>\Hooks;
class MyDataHandlerHooks {}
// <your-ext>/ext_localconf.php
// -> for cmdmap hooks
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processCmdmapClass']['yourextname']
= \Vendor\YourExt\Hooks\MyDataHandlerHooks::class;
// -> for datamap hooks
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tcemain.php']['processDatamapClass']['yourextname']
= \Vendor\YourExt\Hooks\MyDataHandlerHooks::class;
You need to register your class only for these kind of hooks you want to consume. And you do not have to implement all hooks.
Hooks can be looked up in \TYPO3\CMS\Core\DataHandling\DataHandler (as hooks are normally searched.
Next step would be to find the proper hook for your use case, and simply add that hook method to your class. Naming the hooks are not chooseable for DataHandler hooks.
TYPO3 Core tests contains a test fixture class for DataHandler hooks - which is not complete, but contains at least the most common ones (along with the needed method signatures) since 8.x:
https://github.com/TYPO3/typo3/blob/main/typo3/sysext/core/Tests/Functional/DataHandling/DataHandler/Fixtures/HookFixture.php
So you may have to look into the version for your core version to get a feeling how the signature should look for that core version.
Generally I would guess one of these two:
processDatamap_postProcessFieldArray(): Hook with prepared field array, and you can simple add your new stuff to write or update it and it will be saved. Good if you need to change the record directly.
processDatamap_afterDatabaseOperations(): Hook after record has been changed. This is a good startpoint if you need to do other things after saving a record.
Given your usecase, I would tip on the first one, so here a example implementation (in the class and registering as datamap hook as explained above):
// <your-ext>/Classes/Hooks/MyDataHandlerHooks.php
namespace <Vendor>\<YourExt>\Hooks;
class MyDataHandlerHooks {
/**
* #param string|int $id
*/
public function processDatamap_postProcessFieldArray(
string $status, // Status of the current operation, 'new' or 'update'
string $table, // The table currently processing data for
$id, // The record uid currently processing data for,
// [integer] or [string] (like 'NEW...')
array &$fieldArray, // The field array of a record, cleaned to only
// 'to-be-changed' values. Needs to be &$fieldArray to be considered reference.
DataHandler $dataHandler
): void
{
// $fieldArray may be stripped down to only the real fields which
// needs to be updated, mainly for $status === 'update'. So if you
// need to be sure to have correct data you may have to retrieve
// the record to get the current value, if not provided as with new
// value.
if ($table === 'be_users'
&& $status === 'update'
&& array_key_exists('target_field_name', $fieldArray)
) {
$valueToReactTo = $fieldArray['target_field_name'];
if ($valueToReactTo === 'some-check-value') {
// needs not to be there
$fieldArray['update-field'] = 'my-custom-value';
}
}
}
}
How in SonataAdminBundle
get the current admin class without using AdminPool?
Now I'm trying to do it like this
$entityClass = get_class($entity);
$adminClass = $this->adminPool->getAdminByClass($entityClass);
But this method has a problem. If the entity is associated with several classes of the admin, an exception will be thrown.
Is there a way to find out what exactly the admin service should handle the current route?
Thanks!
If you have multiple admins registered for this entity's class, nothing can choose the correct one for you.
You can still get a specific admin with the method Pool::getAdminByAdminCode(string $code).
For example, an usage for you could be:
if ($entityClass === MultipleAdminRegisteredEntity::class) {
$admin = $this->adminPool->getAdminById('specific_admin_id');
} else {
$entityClass = get_class($entity);
$admin = $this->adminPool->getAdminByClass($entityClass);
}
Please pay attention to the fact that the Pool::getAdminByClass(string $class) returns an Admin and not a class string: you named your variable $adminClass which suggests you made this confusion.
Also note that there is an open issue on Github here: https://github.com/sonata-project/SonataAdminBundle/issues/3908 to determine a way to be able define default admins when there are more than one admin for an entity, so that the Pool:getAdminByClass() method doesn't throw an exception. Nobody seems to have care enough about this to implement it, feel free to contribute there if you want.
In my website I want to allow users to view some of the features by using a guest account. The guest account would see much of the same information as a regular user but some things will be limited.
What would be a good approach, programming wise, to this problem?
I copy paste the exact pages that a user would normally have to different .php files, example: guestProfile.php. Then I manually remove the buttons or information a guest would see.
I add a bunch of checks on my views such as:
if(userIsGuest)
// then hide info.
Another solution.
What makes the most logical sense in terms of software engineering? Please try to quantify your answer instead of using opinion.
Abstraction is your friend. Rather than stuffing your views with conditionals, you should pass your views any sub-views you wish to be included in the output. Your view doesn't need to know the inner workings of these sub-views, and you can swap them out depending on whether the user is a normal user or a guest user.
Here's a simplistic example:
class View {
/** Does some view stuff... */
public function addSubview($name, Subview $subview) {
//store subviews to be used in the template
}
}
/**
* Somewhere in a controller far far away...
*/
class GuestController extends Controller {
/** Does some controllery stuff... */
public function exec() {
//..
//prepare the view
$this->view->addSubview('toolbar', $guestToolbarSubview);
}
}
In that case it is better to make the checks (Solution 2). This stays true to the DRY (Don't Repeat Yourself) principle. It keeps your code less redundant and you are less likely to forget to copy changes to your second version of the code.
Try to keep common code in separate php file and include that file wherever required.
I'm trying to add a search form in my jobs view. It's supposed to be a list of all the available jobs along with a search form that filters out the result. The searh form consists out of checkboxes that will need to be prepopulated with the database data.
So I have a number of questions.
Should I accomplish this by using filters or am I looking in the right direction?
Where do I even start, I tried adding the code used for forms in the view below before but I don't even know which class I should inherit from.
Since "JModelList" in combination with getListQuery(). I tried a couple JForm etc and the inherited classes but then I get the following error: "Warning: Invalid argument supplied for foreach() in C:\Users\Nick\Documents\Dropbox\yourstudent\Development\Live\components\com_jobs\views\jobs\tmpl\default.php on line 18"
I'm not having any troubles with creating a form, there are a couple of good examples for the crud operations needed on objects. But not when you either a) combine the list view with the form or b) need the form to act like a search form prepopulated with data.
I'm stuck at combining those 2 views essentially, I can't even test if my form fields etc are correct.
Below is a snippet of the model jobs. Do you guys need any other code for helping me out here?
(This is my first question here, so bear with me and I'll try to adjust my question if needed.)
Code Snippet:
<?php
/* ------------------------------------------------------------------------
# jobs.php - jobs Component
# ------------------------------------------------------------------------
# author Breens Nick
# copyright Copyright (C) 2013. All Rights Reserved
# license GNU/GPL Version 2 or later - http://www.gnu.org/licenses/gpl-2.0.html
# website www.nick.breens.be
------------------------------------------------------------------------- */
// No direct access to this file
defined('_JEXEC') or die('Restricted access');
// import the Joomla modellist library
jimport('joomla.application.component.modellist');
/**
* Jobs Model
*/
class JobsModeljobs extends JModelList {
/**
* Method to build an SQL query to load the list data.
*
* #return string An SQL query
*/
protected function getListQuery() {
// Create a new query object.
$db = JFactory::getDBO();
$query = $db->getQuery(true);
// Select some fields
$query->select('*');
$query->from('#__yourstudent_jobs');
return $query;
}
}
?>
Thanks to a forum post on the Joomla Coding 3.0 subforum I got the answer.
Someone replied telling me that I should use filters in my case and base my component on the articles. I then searched for a good tutorial and then found this 2.5 based tutorial which still applies for 3.0
If anybody is interested I'll post my code below. (The model code remains the same.)
In cake 1.2 there is a feature that allows the developer to no have to create models, but rather have cake do the detective work at run time and create the model for you. This process happens each time and is neat but in my case very hazardous. I read about this somewhere and now I'm experiencing the bad side of this.
I've created a plugin with all the files and everything appeared to be just great. That is until i tried to use some of the model's associations and functions. Then cake claims that this model i've created doesn't exist. I've narrowed it down to cake using this auto model feature instead of throwing and error! So i have no idea what's wrong!
Does anybody know how to disable this auto model feature? It's a good thought, but I can't seem to find where i've gone wrong with my plugin and an error would be very helpful!
There's always the possibility to actually create the model file and set var $useTable = false.
If this is not what you're asking for and the model and its associations actually do exist, but Cake seems to be unable to find them, you'll have to triple check the names of all models and their class names in both the actual model definition and in the association definitions.
AFAIK you can't disable the auto modelling.
Cake 1.2
It's a hack and it's ugly cus you need to edit core cake files but this is how i do it:
\cake\libs\class_registry.php : line 127ish
if (App::import($type, $plugin . $class)) {
${$class} =& new $class($options);
} elseif ($type === 'Model') {
/* Print out whatever debug info we have then exit */
pr($objects);
die("unable to find class $type, $plugin$class");
/* We don't want to base this on the app model */
${$class} =& new AppModel($options);
}
Cake 2
Costa recommends changing $strict to true in the init function on line 95 of Cake\Utility\ClassRegistry.php
See Cake Api Docs for init
ClassRegistry.php - init function
Use
var $useTable = false;
in your model definition.
Delete all cached files (all files under app/tmp, keep the folders)
In most cases where models seem to be acting in unexpected ways, often they dont include changes you've made, it is because that cake is useing an old cached version of the model.
Uh...where do we start. First, as Alexander suggested, clear your app cache.
If you still get the same behaviour, there is probably something wrong with the class and/or file names.
Remember the rules, for controller:
* classname: BlastsController
* filename: blasts_controller.php
for model:
* classname: Blast
* filename: blast.php
Don't foget to handle the irregular inflections properly.