June 30, 2016

How to run Witter app from Udacity “Offline Web Applications” course using docker?

This blog post shows how you can run the Wittr application from a Udacity course called ‘Offline Web Applications’ using docker. This is helpful if your development stack does not run on localhost and you want to overcome `insecure origin` issue of the browsers.

Note: This is blog post, so I have written a bit back story as well. If you don’t want to read about that clone https://github.com/starx/wittr.git run docker-compose up -d Done!

I was trying a course from udacity called “Offline Web Applications” and I got stuck in the point where I was trying to register the service worker. That was because the request to register the service worker was coming form an insecure origin. Nowadays, modern browser browswer have started to deprecate powerful web feature on insecure origin (more here)) and the course fails to address this issue because the developer’s stack can vary. So the question is What is secure origin?

In chrome “Secure origins” are origins that match at least one of the following (scheme, host, port) patterns: more here

(https, *, *)
(wss, *, *)
(*, localhost, *)
(*, 127/8, *)
(*, ::1/128, *)
(file, *, —)
(chrome-extension, *, —) 

So it still supports developer in a way that is does not restrict request from localhost 127... or ::1. Which was another thing to fix because my development stack is not be the most common one as I don’t use localhost to test my work. I do these through vms, vagrant and nowadays from docker as well. This means that most of the time the test url is anything but localhost. So stuck on this for a while I tries several things, like trying to turn off insecure origin flag on flag, proxy, trying to point localhost to another ip is windows 10 bunch of bullshit which is just lost time. So I created a docker configuration to run the witter project.

file: Dockerfile

FROM node:latest

# Create app directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app

# Install app dependencies
COPY ./* /usr/src/app/
RUN npm install

# Bundle app source
COPY . /usr/src/app

EXPOSE 8888 8889
CMD [ "npm run serve" ]

Simple enough for those who know docker basics. It binds with the ports in the host, so the project will run as localhost:8888 and localhost:8889 as the course wants. But if you want to know what it does, I am happy to explain it.

I have forked the course repo and have added this (with a docker-compose.yml as well) to the project itself at:

https://github.com/starx/wittr

Clone https://github.com/starx/wittr.git rather than the one provided in the course and run docker-compose up -d and continue with the course. I have also have created a pull request to help others but there is already 2 previous pull request pending without any comments, So I doubt it will be pulled.

Comments appreciated.

June 8, 2016

How to restrict modification to selected properties of an entity while persisting?

Let’s suppose we have an entity class and a generic FormType corresponding to the entity

class Entry {
    protected $start_time;
    protected $end_time;
    protected $confirmed; // Boolean

    // ... Other fields and getters and setters
}

On the CRUD of this entity, you might not want to allow any modification on start_time or end_time if the entity has been confirmed or in the above case when $confirmed === true

Here are couple of ways this can be solved:

  1. Using the EntityManager you can retrieve original data of the entity. $em->getUnitOfWork()->getOriginalEntityData($entity) returns an array with old data of an entity which can be used to reset the data.

    if($form->isSubmitted() && $editForm->isValid()) {
        // The form was submitted
        if($confirmed === true) { // if the entity was confirmed previously
            $oldData = $em->getUnitOfWork()->getOriginalEntityData($entity);
            $entity->setStartTime($oldData['start_time']);
            $entity->setEndTime($oldData['end_time']);
        }
        // After this you can happily persist the data
        $em->persist($entity);
        $em->flush();
    }
    
  2. Using Form Events

    The following is a Symfony 3 Solution, try this solution for Symfony 2.

    class EntityType extends AbstractType
    {
        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            // ....
            // ....
            // Your form fields
    
            $builder->addEventListener(FormEvents::POST_SET_DATA, array($this, 'onPreSetData'));
        }
    
    
        public function onPreSetData(FormEvent $event) {
            /** @var YourEntity $entity */
            $entity = $event->getData();
            $form = $event->getForm();
    
            if($entity instanceof YourEntity) {
                if ($entity->getTimesheetEntry()->getTimeApproved() === true) {
                    $config = $form->get('start_time')->getConfig();
                    $options = $config->getOptions();
                    $options['disabled'] = true;
                    $form->add('start_time', get_class($config->getType()->getInnerType()), $options);
    
                    $config = $form->get('end_time')->getConfig();
                    $options = $config->getOptions();
                    $options['disabled'] = true;
                    $form->add('end_time', get_class($config->getType()->getInnerType()), $options);
                }
            }
    
        }
    }
    
...

Please fill the form - I will response as fast as I can!