Long story short ;)
Users can upload things to a database but only as long as the admin "allows" it...
For example:
after X minutes, there are no more uplads possible,
or, no more than Y uploads,
or, the admin clicks a link to "enable/disable" the upload-formular..
I cant really find a solution for that, im a beginner... any ideas?
Thanks already
Usually I do this kind of job adding an ACL check inside the validation layer. I suppose you have a validation layer ( I use Zend/Laminas input filter ), so you should have this kind of check in your controller:
$inputFilter->setData($dataFromSomewhere);
$valid = $inputFilter->isValid();
if(!$valid) {
throw new ValidationException(// Error from inputFilter here)
}
Your input filter configuration can have a custom filter with ACL instance ( I use Zend/Laminas ACL ), where you could add a rule with an assertion object ( check the link ) that checks if the current logged user can or cannot do the current API call.
Acl configuration can come from static file or dynamic database rules or both, configuration implementation depends on your needs.
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';
}
}
}
}
I’m implementing follow feature and I want to make sure that
A user should not follow the same user twice
Users should not follow themselves
Should I put the aforementioned conditions insife the follow method ?
class User extends model
{
public function follow($user)
{
if($this->is($user) || $this->isFollowing($user))
{
return;
}
}
}
Or should I validate the given user ?
class FollowController
{
public function store()
{
request()->validate([
'username' => ['exists:users,name', new NotYourself, new AlreadyFollowing]
]);
}
}
Or is there a better way ?
B001 is technically correct, but my opinion is that it's not a matter of opinion. Best-practices would dictate going with the second option. Model classes are better suited for context-agnostic handling of tables, not business-logic. And if you apply your rules in the form of input validation, that gives you access to a few other bells and whistles if the validation fails. Specifically, an error response capable of informing the browser what input field to highlight to tell the user they did something wrong. Taken a step further, I'd suggest placing those rules inside a custom FormRequest object instead of the Controller method.
Locking down the endpoint like this is good practice regardless, to prevent low-level hacks with Postman or some-such. But taking a step further back from that, I'd recommend not offering the UI option in the first place. Check out Gates and Policies. You can write compartmentalized access rules, typically intended for authorization, and use them to dictate whether the "follow" button is available at all.
I have a kind of scavenger hunt project in which I am using AngularJS to manage the different questions it may contain. These questions are of different types. Therefore, some may include a file input and some may not. In this project, I am also using Symfony and SonataMediaBundle to manage my files and my images.
Since my html model (mostly my forms) can change depending on the actions of the user, I cannot use Symfony's built-in tool to produce forms. Therefore, all my forms are custom made. This gives me a problem with SonataMediaBundle, when I want some files to be uploaded. If a user selects a file, this file will be sent via POST to a method in the controller, when the form gets sent. Therefore, I want to send this received file to SonataMediaBundle so that it can manage it, but I haven't found anywhere in the documentation how to do such a thing.
Theoretically, it is really simple. In my controller, when I get a file input, I want to let SonataMedia manage the upload (that is the copy to the proper location, etc...) and I have no clue on how I should do that.
Using symfony2 and not utilizing its benefits you are doing a big mistake you should built your app properly but as far as concerned to your question nothing is bounded by symfony but its on your own how you use it.You can get the sonata media manager service from the container and you have to manually set the all the required setters for the media manager and you have to manually work for the validations like file size ,file mimetype etc. Below is the demo how you can store the file in the sonata media bundle
/* Before class use these*/
use Symfony\Component\HttpFoundation\File\File;
use Symfony\Component\HttpFoundation\File\UploadedFile;
use Application\Sonata\MediaBundle\Entity\Media;
public function uploadAction()
{
$file = $this->get('request')->files->get('filefieldname');
if (!$file instanceof UploadedFile || !$file->isValid()) {
return new Response(json_encode(array(
'event' => 'uploader:error',
'data' => array(
'message' => 'Missing file.',
),
)));
}
/* validate max min size for the file */
/* validate mime type for the file */
/* Get sonata media manager service from container */
$mediaManager = $this->container->get('sonata.media.manager.media');
/* create new instance of sonata media class in my case entity is located at
* Application\Sonata\MediaBundle\Entity\Media
*/
$media = new Media();
$media->setBinaryContent($file);
$media->setContext('default');
$ImagemimeTypes = array('image/jpeg', 'image/png');
$FilemimeTypes = array('application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/msword', 'application/pdf', 'application/x-pdf');
if (in_array($file->getMimeType(), $FilemimeTypes)) {
$media->setProviderName('sonata.media.provider.file');
}
if (in_array($file->getMimeType(), $ImagemimeTypes)) {
$media->setProviderName('sonata.media.provider.image');
}
/*If you have other providers in your app like vimeo /dailymotion etc
then do set here i have shown you the demo for adding image/file in sonata media */
/*other setters if you want to set like enabled/disable etc*/
$mediaManager->save($media);
// end function
}
But once again there will be alot of rework you have to do which symfony already provides you the ease for
The Bundle is there to close exactly this gap between Symfony and SonataMedia. SonataMedia is made for raw PHP, while the SonataMediaBundle attaches Symfony interfaces to SonataMedia; you're rewriting large part of the Bundle's functionality. To get a good example of how it is done right, look at the Bundle's code ;)
Sorry if the next suggestion is something you considered thoroughly, I just cannot be certain based on the limited information in the question. Symfony forms are highly flexible and provide a lot of critical functionality that you should not try to re-implement yourself (like CSRF-tokens). Most problems have a good solution with the form system. If a form is metamorphic (has many constellation of fields, based on UI interaction) and cannot be handled by multiple form types, you still have options. If you can set a GET parameter indicating which type of form is currently being sent then you can pass that to the FormBuilder. You may even choose to build a single big form for the whole application, which contain every field you ever use - that's a bit of a waste but still better than trying to wire posting+sonata together. The most hurt the first version does are some extra bytes and empty field. The least hurt you try could do is introduce serious security leaks.
I have been reading up on DDD a lot over the last few days and could not find one solid example of how someone would go about simply registering a user on their site so after lots of reading I have stuck this together and I would like your feedback on it because I am sure it is far from perfect, it might even be completely wrong but here it goes:
RegisterController
$userMapper = $this->dataMapperFactory->build('user');
if($userMapper->fetchByUsername($username) !== NULL) {
// Error: The chosen username already exists
}
else {
if($userMapper->fetchByEmail($email) !== NULL) {
// Error: The email address already exists
}
else {
$userDO = $this->domainObjectFactory->build('user');
// Set the properties of the $userDO object here with the ones
// from the registration form
// Insert the new user into the database
$userMapper->save($userDO);
}
}
I have done all the form validation with my own FormValidation class so when I add the properties to the $userDO object they are all 100% ready to be inserted into the database (correct length, type, format, ranges etc) so how does the code look to you?
I think I am on the right track and I would really appreciate any tips on how to improve my code.
Also, the way I am checking if the username they chose has already been taken, is there a better way to do that? Instead of having to create an object each time to check? Like the old way I used to do it with a simple:
SELECt COUNT(*) FROM users WHERE username = 'john'
Thanks.
Some theory-related "blah":
As you might be aware, the core concept of MVC and MVC-inspired design patterns is the SoC. It dictates that you divide these patterns in to major layers: presentation layer and domain model layer.
In this case it is significant, because you current structure of controller contains application logic (the interaction domain logic entities and storage abstractions), whereas a controller should be only responsible for altering state of model layer (and sometimes - the current view) based on user input.
You end up violating bot the above mentioned SoC and also SRP.
Note: in context of web based MVC variations the "user" is a web browser, not the person sitting behind it.
Instead you should encapsulate the application logic in services (as #Gordon mentioned). In a fully realized model layer the different services become something like a public-ish API through which the presentation layer interacts with model.
Though, unlink Gordon, I would recommend your service to be a bit broader. In case of user registration, I would make it a part of CommunityService or maybe MembershipService. A structure that handles all the aspects of the user account management as far as the model layer is concerned.
The code bits:
One way for using in controller would look something like:
public function postUser( $request )
{
$community = $this->serviceFactory->build('Community');
$community->addUser( $request->getParameter('username'),
$request->getParameter('password'),
$request->getParameter('repeated_password'),
$request->getParameter('email') );
}
While this is a valid way, you might already notice an possible problem. Even when user registration need only the minimum of data, the amount of parameters that you end up passing to the service makes it hard to use.
Passing the $request on to service is not a valid improvement. You would just end up violating Law of Demeter. Instead i would recommend something like:
$keys = ['username', 'password', 'repeated_password', 'email'];
$community->addUser( $request->getParameters( $keys ) );
Where the getParameters() method is implemented similar to:
public function getParameters( $keys )
{
$response = [];
foreach( $keys as $parameter )
{
$response[ $parameter ] = $this->getParameter( $parameter );
}
return $response;
}
Domain logic and validation
You mentioned, that some FormValidation class, that you are using to make sure, that your instance of User domain object receives proper values. Actually the data validation is one of the domain object's responsibilities. You still might use a separate validation class, to avoid code duplication, but that would be a dependency, which is injected by domain object's factory to share between instances.
Note: in my personal experience, the duplication for validation is quite rare for anything but the null-checks. Each of complicated validation rule-sets are targeted at fields of one specific domain object. That, in my opinion, makes a validation class quite redundant ... unless you expect to share same validation class between multiple projects.
The code-flow usually is such that, when you need to store the data from domain object, you check if it has not acquired an error state, and if there is an error, you actually dump it in session, for a retrieval after redirect.
if ( $user->isValid() )
{
$sqlMapper->store( $user );
}
else
{
$sessionMapper->storeUser();
}
In this use-case pre-validated input ends up actually being harmful.
I'm trying to learn more about the codeigniter framework by porting an existing website to it -- something that's not too complex, or so I thought.
Currently the site is replicated for its users and presents personalized data based on the url, for example, Joe might have his site at:
www.example.com/joe
www.example.com/joe/random-page.php
And you'd replace "joe" with any given user name. The URLs need to be structured this way: /joe/ isnt FOR joe, its for joe's visitors, so I can't rely on a user login or method of this sort. I could switch to joe.example.com but would rather not.
Is there a way I can make this play nice with Code Igniter?
Currently, it would want to call the joe controller. My initial thought is trying to find a way to have a default controller called when a controller doesn't exist, but if some CI pros have advice on a different, better way to handle this, it would be great.
Upgrade to CodeIgniter 2.0 and use $route['404_override'] = 'controller'; or install Modular Extensions which does the same thing, but for now they use $route['404'] instead.
There are a number of different ways to go about this. Just be warned, both of these solutions require you to edit CI's core files. That means you can't upgrade without breaking these edits. Unfortunately hooks do not suitably address this issue.
The easy way:
line 188-195 in system/vodeigniter/CodeIgniter.php handle the logic for what happens when a controller is not found.
The harder but better way:
There is a hook (http://codeigniter.com/user_guide/general/hooks.html)
pre_controller
But this will not work! The reason is that this is called after the controller has been determined, but before anything is actually executed. In other words, it is too late. The next earlier one
pre_system
is in fact too early, because routing has not been done and anything you do the routing will get overwritten.
So I needed the first controller to look the same, yet end up calling a different controller. The reason was that the page was accessed in a hierarchical way, so that it would be the subsection of a subsection and so on.
What I did was add on line 43 of system/libraries/Controller.php
$this->_ci_initialize();
Basically I had it autoload the libraries BEFORE the controller was called, because I was finding that the libraries were not loaded before the controller was called and I needed it to be done so because I needed to check user access authentication and hook directly into the routing itself.
After I did that, I extended one of the native core libraries that were autoloaded (in this case session, for applicaiton specific reasons) and then executed the rerouting.
$RTR = & load_class( 'Router' );
$this->URI = & load_class( 'URI' );
$this->URI->_fetch_uri_string();
I called this code in the start, then put my logic afterwards. This is a sample of what my rerouting logic looks like
if ( $this->segment( 1 ) == 'institute' )
{
if ( ! in_array( $this->segment( 3 ), $course ) )
{
$RTR->set_class( 'courseClass' );
$RTR->set_method( 'index' );
if ( ! $this->segment( 4 ) )
{
$RTR->set_class( 'course' );
$RTR->set_method( 'index' );
}
else
{
$RTR->set_class( 'course' );
$RTR->set_method( $this->segment( 3 ) );
}
}
The original is much longer. I probably should consider writing some sort of plugin or superior way to handle the rewriting rather than silly spagetti logic. However, I needed extremely fine grain control of the controllers being called based on the URLs. This will literally give you god mode control over your controller based on the URLs. Is it a hack? Yes. Is it inelegant? Absolutely. But I needed it done.
Just remember since this edits the core files, you can't easily upgrade after. I think the Kohana framework has a solution to this.
I kept reading the CI docs after Alex's post and found info on the routes.php file which does exactly what I needed.
It allows you to use regular expressions to rewrite the routes (URLs), much in the same manner as mod_rewrite, so I could strip out the user name and end up passing it as a param.