Completed
Push — develop ( 35cb48...6e756f )
by
unknown
15:24 queued 07:54
created

ApplyController::configureContainer()   B

Complexity

Conditions 5
Paths 5

Size

Total Lines 23
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 23
rs 8.5906
cc 5
eloc 14
nc 5
nop 1
1
<?php
2
/**
3
 * YAWIK
4
 *
5
 * @filesource
6
 * @copyright (c) 2013 - 2016 Cross Solution (http://cross-solution.de)
7
 * @license   MIT
8
 */
9
10
/** Applications controllers */
11
namespace Applications\Controller;
12
13
use Applications\Entity\Contact;
14
use Applications\Listener\Events\ApplicationEvent;
15
use Zend\Mvc\Controller\AbstractActionController;
16
use Zend\Mvc\MvcEvent;
17
use Applications\Entity\Application;
18
use Zend\View\Model\ViewModel;
19
use Auth\Entity\AnonymousUser;
20
use Zend\View\Model\JsonModel;
21
use Core\Form\Container;
22
use Core\Form\SummaryForm;
23
use Core\Entity\PermissionsInterface;
24
use Applications\Entity\Status;
25
use Applications\Entity\StatusInterface;
26
27
/**
28
 * there are basically two ways to use this controller,
29
 * (1) either you have a form, and want to accumulate inputs, or you want to create a form on an existing or new application
30
 * (2) or want to do some action on a concrete form.
31
 *
32
 * for both you need the applyId, which is NOT the application-id, the applyId is an alias for the Job, it can be some human-readable text or an external ID.
33
 * the application to an applyId is found by the combination of the user and the job, that is represented by the applyId.
34
 * this approach ensures, that applications stick to the related job.
35
 *
36
 * nonetheless, there is an exception, for the posts for updating the application, the application-id is needed.
37
 *
38
 * if you use the do as query-parameter, you have to customize the do-Action for the special purpose that is assigned to the do parameter in the query
39
 *
40
 * @method \Acl\Controller\Plugin\Acl acl()
41
 * @method \Core\Controller\Plugin\Notification notification()
42
 * @method \Core\Controller\Plugin\Mailer mailer()
43
 * @method \Auth\Controller\Plugin\Auth auth()
44
 * @author Mathias Gelhausen <[email protected]>
45
 */
46
class ApplyController extends AbstractActionController
47
{
48
    
49
    protected $container;
50
    
51 View Code Duplication
    public function attachDefaultListeners()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
52
    {
53
        parent::attachDefaultListeners();
54
        $events = $this->getEventManager();
55
        $events->attach(MvcEvent::EVENT_DISPATCH, array($this, 'preDispatch'), 10);
56
        $serviceLocator  = $this->getServiceLocator();
57
        $defaultServices = $serviceLocator->get('DefaultListeners');
58
        $events->attach($defaultServices);
0 ignored issues
show
Documentation introduced by
$defaultServices is of type object|array, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
59
        return $this;
60
    }
61
    
62
    public function preDispatch(MvcEvent $e)
63
    {
64
        /* @var $application \Applications\Entity\Application */
65
        if ($this->params()->fromQuery('do')) {
66
            $e->getRouteMatch()->setParam('action', 'do');
67
            return;
68
        }
69
70
        /* @var $request    \Zend\Http\Request */
71
        /* @var $repository \Applications\Repository\Application */
72
        /* @var $container  \Applications\Form\Apply */
73
        $request      = $this->getRequest();
74
        $services     = $this->getServiceLocator();
75
        $repositories = $services->get('repositories');
76
        $repository   = $repositories->get('Applications/Application');
77
        $container    = $services->get('forms')->get('Applications/Apply');
78
        
79
        if ($request->isPost()) {
80
            $appId = $this->params()->fromPost('applicationId');
81
            if (!$appId) {
82
                throw new \RuntimeException('Missing application id.');
83
            }
84
            $routeMatch = $e->getRouteMatch();
85
86
            if ('recruiter-preview' == $appId) {
87
                $routeMatch->setParam('action', 'process-preview');
88
                return;
89
            }
90
91
            $application = $repository->find($appId);
92
            if (!$application) {
93
                throw new \RuntimeException('Invalid application id.');
94
            }
95
96
            $action     = 'process';
97
98
            $routeMatch->setParam('action', $action);
99
        } else {
100
            $user  = $this->auth()->getUser();
101
            $appId = $this->params('applyId');
102
            if (!$appId) {
103
                throw new \RuntimeException('Missing apply id');
104
            }
105
106
            /* @var \Jobs\Entity\Job $job */
107
            $job = $repositories->get('Jobs/Job')->findOneByApplyId($appId);
108
109
            if (!$job) {
110
                $e->getRouteMatch()->setParam('action', 'job-not-found');
111
                return;
112
            }
113
114
            switch ($job->getStatus()) {
115
                case \Jobs\Entity\Status::ACTIVE:
116
                    break;
117
                default:
118
                    $e->getRouteMatch()->setParam('action', 'job-not-found');
119
                    return;
120
                    break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
121
            }
122
123
            if ($user === $job->getUser()) {
124
                $application = new \Applications\Entity\Application();
125
                $application->setContact(new Contact());
126
                $application->setJob($job);
127
                $application->setId('recruiter-preview');
128
            } else {
129
                $subscriberUri = $this->params()->fromQuery('subscriber');
130
                $application   = $repository->findDraft($user, $appId);
131
132
                if ($application) {
133
                    /* @var $form \Auth\Form\UserInfo */
134
                    $form = $container->getForm('contact.contact');
135
                    $form->setDisplayMode('summary');
136
137
                    if ($subscriberUri) {
138
                        $subscriber = $application->getSubscriber();
139
                        if (!$subscriber || $subscriber->uri != $subscriberUri) {
140
                            $subscriber = $repositories->get('Applications/Subscriber')->findbyUri($subscriberUri, /*create*/ true);
141
                            $application->setSubscriber($subscriber);
142
                            $subscriber->getname();
143
                        }
144
                    }
145
                } else {
146
                    if (!$job) {
147
                        $e->getRouteMatch()->setParam('action', 'job-not-found');
148
                        return;
149
                    }
150
                    if ($job->getUriApply()) {
151
                        return $this->redirect($job->getUriApply());
0 ignored issues
show
Unused Code introduced by
The call to ApplyController::redirect() has too many arguments starting with $job->getUriApply().

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
152
                    }
153
154
                    /* @var $application \Applications\Entity\Application */
155
                    $application = $repository->create();
156
                    $application->setIsDraft(true)
157
                                ->setContact($user->getInfo())
158
                                ->setUser($user)
159
                                ->setJob($job);
160
161
                    if ($subscriberUri) {
162
                        $subscriber = $repositories->get('Applications/Subscriber')->findbyUri($subscriberUri, /*create*/ true);
163
                        $application->setSubscriber($subscriber);
164
                    }
165
166
                    $repositories->store($application);
167
                    /*
168
                     * If we had copy an user image, we need to refresh its data
169
                     * to populate the length property.
170
                     */
171
                    if ($image = $application->getContact()->getImage()) {
172
                        $repositories->refresh($image);
173
                    }
174
                }
175
            }
176
        }
177
        
178
        $container->setEntity($application);
179
        $this->configureContainer($container);
180
        $this->container = $container;
181
    }
182
    
183
    public function jobNotFoundAction()
184
    {
185
        $this->response->setStatusCode(410);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Zend\Stdlib\ResponseInterface as the method setStatusCode() does only exist in the following implementations of said interface: Zend\Http\PhpEnvironment\Response, Zend\Http\Response, Zend\Http\Response\Stream.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
186
        $model = new ViewModel(
187
            [ 'content' => /*@translate*/ 'Invalid apply id']
188
        );
189
        $model->setTemplate('applications/error/not-found');
190
        return $model;
191
    }
192
    
193
    public function indexAction()
194
    {
195
        /* @var \Applications\Form\Apply $form */
196
        $form        = $this->container;
197
        $serviceLocator = $this->getServiceLocator();
198
        $translator = $serviceLocator->get('Translator');
199
        /* @var \Auth\Form\SocialProfiles $profiles */
200
201
        $profiles=$form->get('profiles');
202
        /*
203
         * can we add the description to Applications\Form\Apply ?
204
         */
205
        $profiles->getBaseFieldset()->setOption('description', $translator->translate("you can add your social profile to your application. You can preview and remove the attached profile before submitting the application."));
206
207
        $application = $form->getEntity();
208
        
209
        $form->setParam('applicationId', $application->id);
0 ignored issues
show
Bug introduced by
Accessing id on the interface Core\Entity\EntityInterface suggest that you code against a concrete implementation. How about adding an instanceof check?

If you access a property on an interface, you most likely code against a concrete implementation of the interface.

Available Fixes

  1. Adding an additional type check:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeInterface $object) {
        if ($object instanceof SomeClass) {
            $a = $object->a;
        }
    }
    
  2. Changing the type hint:

    interface SomeInterface { }
    class SomeClass implements SomeInterface {
        public $a;
    }
    
    function someFunction(SomeClass $object) {
        $a = $object->a;
    }
    
Loading history...
210
211
        $model = new ViewModel(
212
            array(
213
            'form' => $form,
214
            'isApplicationValid' => $this->checkApplication($application),
215
            'application' => $application,
216
            )
217
        );
218
        $model->setTemplate('applications/apply/index');
219
        return $model;
220
    }
221
222
    public function processPreviewAction()
223
    {
224
        return new JsonModel(array('valid' => false, 'errors' => array()));
225
    }
226
    
227
    public function processAction()
0 ignored issues
show
Coding Style introduced by
processAction uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
processAction uses the super-global variable $_FILES which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
228
    {
229
        $formName  = $this->params()->fromQuery('form');
230
        $form      = $this->container->getForm($formName);
231
        $postData  = $form->getOption('use_post_array') ? $_POST : array();
232
        $filesData = $form->getOption('use_files_array') ? $_FILES : array();
233
        $data      = array_merge($postData, $filesData);
234
235
        $form->setData($data);
236
        
237 View Code Duplication
        if (!$form->isValid()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
238
            return new JsonModel(
239
                array(
240
                'valid' => false,
241
                'errors' => $form->getMessages(),
242
                )
243
            );
244
        }
245
        $application = $this->container->getEntity();
246
        $this->getServiceLocator()->get('repositories')->store($application);
247
        
248
        if ('file-uri' === $this->params()->fromPost('return')) {
249
            $basepath = $this->getServiceLocator()->get('ViewHelperManager')->get('basepath');
250
            $content = $basepath($form->getHydrator()->getLastUploadedFile()->getUri());
251 View Code Duplication
        } else {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
252
            if ($form instanceof SummaryForm) {
253
                $form->setRenderMode(SummaryForm::RENDER_SUMMARY);
254
                $viewHelper = 'summaryform';
255
            } else {
256
                $viewHelper = 'form';
257
            }
258
            $content = $this->getServiceLocator()->get('ViewHelperManager')->get($viewHelper)->__invoke($form);
259
        }
260
        
261
        return new JsonModel(
262
            array(
263
            'valid' => $form->isValid(),
264
            'content' => $content,
265
            'isApplicationValid' => $this->checkApplication($application)
266
            )
267
        );
268
    }
269
    
270
    public function doAction()
271
    {
272
        $services     = $this->getServiceLocator();
273
        $config       = $services->get('Config');
274
        $repositories = $services->get('repositories');
275
        $repository   = $repositories->get('Applications/Application');
276
        /* @var Application $application*/
277
        $application  = $repository->findDraft(
278
            $this->auth()->getUser(),
279
            $this->params('applyId')
280
        );
281
        
282
        if (!$application) {
283
            throw new \Exception('No application draft found.');
284
        }
285
        
286
        if ('abort' == $this->params()->fromQuery('do')) {
287
            $repositories->remove($application);
288
            return $this->redirect()->toRoute('lang/apply', array('applyId' => $this->params('applyId')));
289
        }
290
        
291
        if (!$this->checkApplication($application)) {
292
            $this->notification()->error(/*@translate*/ 'There are missing required informations. Your application cannot be send.');
293
            return $this->redirect()->toRoute('lang/apply', array('applyId' => $this->params('applyId')));
294
        }
295
296
        if ('sendmail' == $this->params()->fromQuery('do')) {
297
            $jobEntity         = $application->job;
298
            ;
299
            $mailData = array(
300
                'application' => $application,
301
                'to'          => $jobEntity->contactEmail
302
            );
303 View Code Duplication
            if (array_key_exists('mails', $config) && array_key_exists('from', $config['mails']) && array_key_exists('email', $config['mails']['from'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
304
                $mailData['from'] = $config['mails']['from']['email'];
305
            }
306
            $this->mailer('Applications/CarbonCopy', $mailData, true);
0 ignored issues
show
Unused Code introduced by
The call to ApplyController::mailer() has too many arguments starting with 'Applications/CarbonCopy'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
307
            $repositories->remove($application);
308
            //$this->notification()->success(/*@translate*/ 'Application has been send.');
0 ignored issues
show
Unused Code Comprehensibility introduced by
53% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
309
            $model = new ViewModel(
310
                array(
311
                'success' => true,
312
                'job' => $jobEntity,
313
                )
314
            );
315
            $model->setTemplate('applications/apply/success');
316
            return $model;
317
        }
318
319
        $application->setIsDraft(false)
320
            ->setStatus(new Status())
321
            ->getPermissions()
322
            ->revoke($this->auth()->getUser(), PermissionsInterface::PERMISSION_CHANGE)
323
            ->inherit($application->getJob()->getPermissions());
324
325
        $events   = $services->get('Applications/Events');
326
        $events->trigger(ApplicationEvent::EVENT_APPLICATION_POST_CREATE, $this, [ 'application' => $application ]);
327
328
        $model = new ViewModel(
329
            array(
330
            'success' => true,
331
            'application' => $application,
332
            )
333
        );
334
        $model->setTemplate('applications/apply/index');
335
336
        return $model;
337
    }
338
339
    protected function checkApplication($application)
340
    {
341
        return $this->getServiceLocator()->get('validatormanager')->get('Applications/Application')
342
                    ->isValid($application);
343
    }
344
345
    /**
346
     * Configures the apply form container.
347
     *
348
     * Currently only disables elements.
349
     *
350
     * @param Container $container
351
     */
352
    protected function configureContainer(Container $container)
353
    {
354
        /* @var $application Application */
355
        $application = $container->getEntity();
356
        $job         = $application->getJob();
357
358
        /** @var $settings \Applications\Entity\Settings */
359
        $settings = $job->getUser()->getSettings('Applications');
360
        $formSettings = $settings->getApplyFormSettings();
361
362
        if ($formSettings && $formSettings->isActive()) {
363
            $container->disableElements($formSettings->getDisableElements());
364
            return;
365
        }
366
367
        $config = $this->getServiceLocator()->get('Config');
368
        $config = isset($config['form_elements_config']['Applications/Apply']['disable_elements'])
369
                ? $config['form_elements_config']['Applications/Apply']['disable_elements']
370
                : null;
371
        if ($config) {
372
            $container->disableElements($config);
373
        }
374
    }
375
}
376