Authorisation on Roles in Symfony2

Following a discussion on the IRC room from someone who viewed my post about login/logout handlers in SF2, i wanted to clarify that when dealing with Roles, they should be dealt with in the controller actions.

For off, you need to make sure you got the right hierarchy of roles. In your app/config/security.yml you need something following the structure of:

security:
    role_hierarchy:
        ROLE_USER:          ROLE_USER
        ROLE_MODERATOR:     [ROLE_USER]
        ROLE_ADMIN:         [ROLE_USER, ROLE_MODERATOR]
        ROLE_SUPER_ADMIN:   [ROLE_USER, ROLE_MODERATOR, ROLE_ADMIN]
									

Where the lowest level of access is at the top and the higher levels of access envelope the last levels of access.

Then you inside your controller actions you would do:

class fooController
{
  public function showAction()
  {
    if ( ! $this->container->get('security.context')->isGranted('ROLE_USER')) {
      throw new AccessDeniedException('You do not have permission to use this resource!');
    }
  }
}
									

According the order of the role hierarchy if you have the role or higher than specified the controller action will continue, if you have a lower level of access than the minimum required then you will get an AccessDeniedException.

Apr30

Git workflow.

1) create a branch.

git checkout -b feature/newFeature

2) add new files and commit.

git add ./*

git commit -a

3) push to github.

git push -u origin feature/newFeature

4) switch to new branch of github then PR and merge.

5) pull update from github with  merged changes.

git pull origin master

Apr10

Deploying SF2 Projects on Plesk VPS.

Having recently setup the new codeconsortium.com website i thought i would share some of the steps i took to get things working as it was not all smooth sailing. First off i just to clarify the platform i am using, it is a linux setup utilising Plesk and a typical LAMP setup with that.

My project written in Symfony2 and all custom code (minus FOSUserBundle), did not get off to a great start on deployment, so for other people deploying their web apps on a VPS running plesk i will share the steps i took to get things working.

First you need to obviously create your account and you can do that under Plesk control panel, this is a relatively simple step and as we are more concerned about the more complex arena of what we need to do on the command line i shall leave the creating of the account under Plesk to the documentation that it ships with.

Once you have your account setup and you have your SSH credentials handy we can begin. Firstly, download the Symfony2 framework from their site preferably WIHOUT vendors, this will greatly reduce the transfer time of things if you are going to just FTP them onto your server, but you could use git to do this also.

If you don’t have git i suggest using yum to install git. You can do this with:

$ yum install git

Once git is installed you can download SF2 that way, otherwise just FTP SF2 onto the server, preferably inside your httpdocs directory.

The next steps we need to take is to resolve some issues with the default setup of vhost.conf, which should be located in your domains conf/vhost.conf. Once you are in your domain dir (something like /var/www/vhosts/YOUR-DOMAIN.COM/) you should find the file which you can open with the vi editor like so:

$ vi vhost.conf

Then we need to make some additions, which i shall explain after this snippet, you need to edit your vhost.conf file to look like this (codeconsortium.com is my own domain, substitute this with your own accordingly.):

<Directory /var/www/vhosts/codeconsortium.com/httpdocs>
php_admin_flag engine on
php_admin_flag safe_mode off
php_admin_value open_basedir /var/www/vhosts/codeconsortium.com/:/tmp:

Options +FollowSymLinks

AllowOverride AuthConfig FileInfo
Order deny,allow
Allow from all
</Directory>

DocumentRoot /var/www/vhosts/codeconsortium.com/httpdocs/symfony/web
									

A few notes on the above snippet now, firstly, the most common issue people have is with open_basedir, which has a tendency to create permission errors with scripts, usually resulting in a blank screen with no error output. The other issue we resolve is to allow scripts and resources to be accessible through symlinks (particularly useful for assets where it is more efficient to symlink your bundles assets than copy them, particularly when you update your bundles later on). Then the section after regarding AllowOverride etc, opens up some of the remaining permissions issues. The last part of this vhost.conf allows us to use thw web folder as our actual web root directory, thusly protecting the rest of your source code (particularly your configs containing database passwords etc) from outside snooping. Once this is done, just restart apache like so:

$ service httpd restart

Now that your vhost.conf configuration is out of the way, you will need to upload your project to your symfony/src/ directory on your server. You can do this either through FTP or the SCP utility on the command line, its up to you. I personally prefer using Cyberduck FTP client because you can add all manner of file types to exclude, namely large revision control files such as .git files etc. Excluding such files can cut your upload time in half in some projects with very large repositories.

Once your project is uploaded successfully, we should run the vendors install script, which you can do via:

$ php bin/vendors install

This will install all the vendors that you did not download from the SF2 website. It makes much more sense to use the power of your VPS’s large bandwidth to download vendors than to download them to your local work machine and then use what is usually a very poor upload bandwidth of your ISP to get it all onto the server.

Before we run the check.php utility we will want to set the proper permissions on both the cache and log directories, so from your symfony/app directory do the following:

$ chmod 777 cache
$ chmod 777 logs

This is necessary so that symfony can write to the cache and logs directories. Once that is done, we must now run the check utility from the command line also, this helps identify any issues with your setup which will likely be a few on there. To run this type:

$ php app/check.php

Following the issues highlighted modify your php.ini accordingly, usually you will do this by opening it in vi, and your default php.ini is usually in /etc/php.ini but we don’t want to edit this one, we want to edit your local php.ini which will overwrite the default one. If you choose to edit the default one found in /etc/php.ini then any changes made will effect all users on that server, depending on your root level of access on your VPS you may not actually be able to edit this anyway.

To edit your local php.ini file using vi again, edit:

$ vi /var/www/vhosts/YOUR-DOMAIN.COM/etc/php.ini
OR if you do not have root access it will just be
$ vi etc/php.ini

And make all the according changes as per the issues the check.php script mentioned. Then we restart apache again:

$ service httpd restart

Just to be sure that we got everything right, we can test that our settings are the right ones being used by creating a little test script in our httpdocs/symfony/web directory, create a file called phpinfo.php and edit it to look like the following:

$ vi phpinfo.php

<?php echo phpinfo(); ?>
									

Once all of those issues are resolved, we now want to make sure that there are no funky ‘.htaccess’ files in your root dir that might interfere with your project, so cd into your domains httpdocs directory and locate the default .htaccess directory, and delete it, if you don’t find one in there then that is good also. DO NOT remove the .htaccess file found in symfony/web you need this.

Now go to your browser and under your domain run your script, e.g; www.your-domain.com/phpinfo.php. This should output a really nice looking page with all the configs of php. Check for all the appropriate settings and check all the php.ini includes you see on the page are correct. Hopefully the chosen php.ini should have its configs overwriting the default ones, if not see what php.ini file your setup is favouring and edit that accordingly.

Now we should go back to our symfony directory and setup our entities and database stuff. Go to your symfony/app/config directory and edit your parameters.ini file so that you have input all your database credentials. If you don’t have any database credentials then you need to go into your Plesk control panel and create a new database and get your login credentials from there.

$ vi parameters.ini

To implement this you now need to run these 2 commands (the first one you will need to run for each bundles group namespace):

$ php app/console generate:doctrine:entities <your-bundles-namespace>

$ php app/console doctrine:schema:update

That should be your database setup now, now all you have to do is install your assets. As we enabled the FollowSymLinks setting under our vhost.conf this should be no problem, just run the command below:

$ php app/console assets:install –symlink web/

Now that this is done, we should be ready to test our site out, check your domain to see if you can access your site.

Hopefully all is well and you don’t have any issues, but if you do, check your error log, which you can find in your domain’s statistics directory, statistics/logs/error_log if you do not have a statistics directory then check /var/log/httpd/error_log and see what issues are coming up there. The last occuring issues will be at the very bottom of the error log, so look their first.

Assuming all is well, we can now delete the phpinfo.php script (its not good to leave this around, it can be a security hazard to leak all of your setup info)

$ rm phpinfo.php

Also remove app_dev.php and config.php from your web directory as these are only needed for dev environments.

$ rm config.php app_dev.php

I would also recommend once everything is working and you have tested out everything on your site that you further edit your php.ini file in your local domain area so that you set the error reporting to:

error_reporting = E_ALL & ~E_DEPRECATED
display_errors = Off
display_startup_errors = Off
									

but keep ‘log_errors = On‘ as you will want a log of anything that is breaking down if someone reports it then you can trace the log to duplicate the conditions hopefully under which the bug was generated, or at least point you in the right direction.

The last step is to setup some additional tools, namely php’s APC, which is really useful for optimising your sites php performance overall. Though installing APC is not required, it can dramatically improve performance and is recommended. You can read about that in my other blog article http://www.reecefowell.com/2012/01/17/installing-apc-on-plesk/

Jan21

DateTime::__construct(): It is not safe to rely on the system’s timezone settings.

If your getting the above error, then you have a misconfiguration in your php.ini file, this is very easy to remedy. Firstly find out which php.ini file your setup is using, you can do this using phpinfo(), just echo that out on its own in a plain php test script, like so:

<?php echo phpinfo(); ?>
									

Then locate where it mentions what php.ini you are using (do a page search on the output), and then load that php.ini file up and look for the line that states:

date.timezone
it may be commented out like this ;date.timezone. Once you find it, set it to your region and capitol, as i have here:

date.timezone = ‘Europe/London’
And ensure that the semicolon is not at the beginning of this line. Save the file then restart your web servers Apache/PHP and the following error you saw should go away.

DateTime::__construct(): It is not safe to rely on the system's timezone settings. You are *required* to use the date.timezone setting or the date_default_timezone_set() function. In case you used any of those methods and you are still getting this warning, you most likely misspelled the timezone identifier. We selected 'Europe/Berlin' for 'CET/1.0/no DST' instead  
									

Enjoy.

Jan17

Installing APC on a server running Plesk

I needed to install APC on a Plesk VPS today, and thought i would share what i did. Following the guide in the blog below i made some additions for a few things that were missing.

Original Guide: http://2bits.com/articles/installing-php-apc-gnulinux-centos-5.html

I also happen to be running CentOS, version 6.

Following the same steps in the linked site, i will add the few that you need that were missed and hopefully this will help anyone else who had the same issues i did with this. (if your like me an impatient you can add -y for most of these to speed up the setup process, it just auto answer yes to any yes/no prompts [mostly do you want to download this? sort of things]).

SSH into your server, preferably with some kind of admin/root access, and then type in the following one at a time.

$ yum install -y php-pear
$ yum install -y php-devel
$ yum install -y httpd-devel
Now we need to install the following as some installations of CentOS don’t have this but you may require it to use some of the PECL extensions.
$ yum install -y pcre-devel
Now chances are being a web server your setup most likely won’t have a dev tool chain, so we need 2 more things before we install APC. We need a compiler and we need a makefile utility (make will do). But first you can check if you have them before going to the extent of downloading something you may not need, to check if you got them:
$ which gcc
$ which make
If any of the 2 above do not show any results and comes back with nothing then you do not have them and need to install them, if your missing just one then install the one your missing. You will need both of them for the next step, if it comes up with a directory then you have it installed. If not we will get them with:
$ yum install -y gcc
$ yum install -y make
Now we should be able to download and install APC (don’t use the -y option here):
$ yum install apc
Now we need to create the extensions configs, first we create the ini file we need
$ touch /etc/php.d/apc.ini
Then edit the file in vi (i would recommend vi over nano/pico as they tend to glitch over ssh)
$ vi /etc/php.d/apc.ini
Now type this into the vi editor (you need to press ‘i’ before you can type)

extension=apc.so
[apc]
apc.cache_by_default=0 # disable by default.
									


So save this file press the escape key and then type :wq and press enter, which should save and quit vi. I choose to disable the cache by default because this setting will apply to all users of your server and it can be a pain if people don’t want it running, they can enable it for themselves in their own directories etc/apc.ini

Lastly we need to restart the http server.
$ service httpd restart

Jan17

Redirecting on login/logout in Symfony2 using LoginHandlers.

Using login handlers or even logout handlers, we can do a number of last minute things before the user is redirected to where they need to go. Being able to intercept the redirect at the last minute and make the user get redirected elsewhere is relatively simple.

Firstly though, i notice a lot of people now who are using Symfony 2 are cheating somewhat by using an EventListener and registering it with the security.interactive_login event listener. This is not the best way to go about this, namely because this event listener is not intended for this purpose. Sometimes event listeners are not the best way to go because they were defined for a specific purpose other than your intention and using them may have undesirable side effects you may not notice right away. Using handlers however allows a little more flexibility in that in all likelihood regardless of changes to Symfony in future revisions, they should still work and were designed specifically for the purposes we will need them for.

Firstly, we need to define our services, personally, i am a fan of YAML, so i define my services as such but feel free to do the same in XML:

parameters:
    code_consortium_user.login_success_handler.class: CodeConsortiumUserBundleServiceLoginSuccessHandler
    code_consortium_user.logout_success_handler.class: CodeConsortiumUserBundleServiceLogoutSuccessHandler
    
services:
    login_success_handler:
        class:  %code_consortium_user.login_success_handler.class%
        arguments:  [@router, @security.context]
        tags:
            - { name: 'monolog.logger' channel: 'security' }
    logout_success_handler:
        class:  %code_consortium_user.logout_success_handler.class%
        arguments:  [@router]
        tags:
            - { name: 'monolog.logger' channel: 'security' }  
									

Now our services are defined, we need to define the handlers themselves. I like to create a directory named Services in my user bundle for this purpose, then create 2 files. The first file is LoginSuccessHandler, the second is LogoutSuccessHandler. Our handlers will implement the appropriate interface for either our login or logout handlers, which require usually only one method in these instances.

Here is what the login handler should look like:

<?php

namespace CodeConsortiumUserBundleService;

use SymfonyComponentSecurityHttpAuthenticationAuthenticationSuccessHandlerInterface;
use SymfonyComponentSecurityCoreAuthenticationTokenTokenInterface;
use SymfonyComponentSecurityCoreSecurityContext;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentHttpFoundationRedirectResponse;
use SymfonyComponentRoutingRouter;

class LoginSuccessHandler implements AuthenticationSuccessHandlerInterface
{
  
  protected $router;
  protected $security;
  
  public function __construct(Router $router, SecurityContext $security)
  {
    $this->router = $router;
    $this->security = $security;
  }
  
  public function onAuthenticationSuccess(Request $request, TokenInterface $token)
  {
    
    if ($this->security->isGranted('ROLE_SUPER_ADMIN'))
    {
      $response = new RedirectResponse($this->router->generate('category_index'));      
    }
    elseif ($this->security->isGranted('ROLE_ADMIN'))
    {
      $response = new RedirectResponse($this->router->generate('category_index'));
    } 
    elseif ($this->security->isGranted('ROLE_USER'))
    {
      // redirect the user to where they were before the login process begun.
      $referer_url = $request->headers->get('referer');
            
      $response = new RedirectResponse($referer_url);
    }
      
    return $response;
  }
  
}
									

Note my namespace is CodeConsortium, and my bundle is called UserBundle, so our service is named code_consortium_user. Change this to your own namespace and bundle name accordingly but ensure to use the same format as used here, underscore your namespace and append the bundle name with an additional underscore without the term ‘bundle’ in it. Failure to get this part correct will mean your namespace will not be loaded and you will get problems later on in this tutorial as exceptions will be thrown regarding the service not existing.

Here is what the logout handler should look like:

<?php

namespace CodeConsortiumUserBundleService;

use SymfonyComponentSecurityHttpLogoutLogoutSuccessHandlerInterface;
use SymfonyComponentSecurityCoreAuthenticationTokenTokenInterface;
use SymfonyComponentHttpFoundationRequest;
use SymfonyComponentHttpFoundationRedirectResponse;
use SymfonyBundleFrameworkBundleRoutingRouter;

class LogoutSuccessHandler implements LogoutSuccessHandlerInterface
{
  
  protected $router;
  
  public function __construct(Router $router)
  {
    $this->router = $router;
  }
  
  public function onLogoutSuccess(Request $request)
  {
    // redirect the user to where they were before the login process begun.
    $referer_url = $request->headers->get('referer');
          
    $response = new RedirectResponse($referer_url);    
    return $response;
  }
  
}
									

Our service definitions inject some of the classes we need to make use of our handlers, namely a Router object and the SecurityContext object, we can store these in member variables for later use. Then we wait for the onAuthenticationSuccess method to be called in our LoginSuccessHandler, once this has happened, we have a request and token interface object at our disposal. Using the security context object we can determine what role they have (i.e; ROLE_USER, ROLE_ADMIN etc) and appropriately redirect them as needed.

I like to redirect members of ROLE_USER role to where they came from prior to logging in. This is achieved by passing the ‘referer’ header into the redirect response object we return. I took this step both in the login and logout handlers.

Last thing we now need to do, is inform our security config that it needs to use these handlers by doing the following:

security:
    providers:
        fos_userbundle:
            id: fos_user.user_manager

    firewalls:
        main:
            #pattern:    .*
            form_login:
                provider:       fos_userbundle
                login_path:     /login
                use_forward:    false
                check_path:     /login_check
                success_handler: login_success_handler
                failure_path:   null
            logout:
                path:   /logout
                target: /
                success_handler: logout_success_handler
            anonymous:  true
									

In this instance i am using FOSUserBundle and extending it in my own UserBundle, which is probably the best way to do this but if you are using another user bundle or have written your own this should still work, just change mention of FOSUserBundle to reference your own.

Once this is implemented your on your way to a better login/logout system. Enjoy.

Oct26

Symfony Embedded Forms ‘take 2′

Learnt some new things while working with symfony’s embedded forms, as we know, when they are have relations in the entities that the form is modelled after you want to ensure that when including the other form type that you create an instance of the formType representing the many in the manyToOne relationship. For example in the forum i am working on (which you can find on github http://github.com/reecefowell/CodeConsortiumForum though currently still in development as of the date of this blog entry) a Topic has MANY Posts, as a result, we build our form by instantiating the PostType first and then adding the TopicType to it as a field because the PostType has a related Topic in the database schema.

But following up from the last post, what about when we have unique use cases for our form Types? What if we want to use a PostType without including the TopicType or we wish to conditionally decide wether for the purposes of editing we need to populate the form before presenting it?

To make this easier, in my FormHandlers what i choose to do is pass an array with some options we can use, namely a mode key specifies wether we are to ‘insert’ or ‘update’ an entity, and other keys for holding the entity to populate the form if mode is ‘update’

Here is an example from the top half of my TopicFormHandler:

class TopicFormHandler
{
  protected $factory;
  protected $request;
  protected $options;
  protected $form;

  public function __construct($factory, Request $request, array $options = null )
  {
    $this->factory = $factory;
    $this->request = $request;
    $this->options = $options;
    
    if ($this->options['mode'] == 'update')
    {
      $this->form = $factory->create(new PostType(), $this->options['post']);
      $this->form->add($factory->create(new TopicType(), $this->options['post']->getTopic()));
    }
    else
    {
      $this->form = $factory->create(new PostType());
      $this->form->add($factory->create(new TopicType()));
    }
  }
									

To view the full source in greater context, explore the source on github.

http://github.com/reecefowell/CodeConsortiumForum

Oct18

Creating embedded forms in Symfony 2.0

If you are looking to create a form from 2 or more entities, like i have been then you probably find the documentation somewhat lacking and confusing. If you follow the guides you can get your form working if you start from the least dependant entity making that the first entity you use to build your form from your controller. I find if you start from the most dependant you usually get all manner of errors.

What i have is a basic forum i am working on, in my current form situation i have 3 entities, Board entity, a Thread entity and a Post entity. If you start by creating your form with Board then chances are it won’t work, i created my forms in separate ‘Type’ classes (PostType.php ThreadType.php and BoardType.php) and then created my PostType form in my controller, which then sorts out its dependencies on its own.

My controller class is very basic and not very polished, but here is what it looks like:

 // In your controller 
 public function createAction($board_id)
 {
 $board = $this->getDoctrine()
 ->getRepository('CodeConsortiumForumBundle:Board')
 ->find($board_id);

 $post = new post();

 $form = $this->createForm(new PostType(), $post);

 $formHandler = new PostFormHandler($form, $this->getRequest(), $post);

 if ($formHandler->process()) 
 {
 $em = $this->get('doctrine')->getEntityManager();
 $em->persist($form->getData());
 $em->flush();

 //return $this->redirect($this->generateUrl('thread_show', array( 'thread_id' => $thread_id ) ));
 }
 else
 {
 return $this->render('CodeConsortiumForumBundle:Thread:create.html.twig', array(
 'board' => $board,
 'form' => $form->createView(),
 ));
 }

 }
									

It probably looks somewhat messy as i say, its a rough work in progress, but it works, and here are the FormTypes i used, starting with; PostType.php:

<?php
// CodeConsortium/ForumBundle/Form/Type/PostType.php
namespace CodeConsortiumForumBundleFormType;

use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilder;

class PostType extends AbstractType
{

 private $options;

 public function __construct(array $options = null)
 {
 $this->options = $options;
 }

 public function buildForm(FormBuilder $builder, array $options)
 {
 $builder->add('Thread', new ThreadType($this->options) );
 $builder->add('body');
 }

 // for creating and replying to threads
 public function getDefaultOptions(array $options)
 {
 return array(
 'data_class' => 'CodeConsortiumForumBundleEntityPost',
 );
 }

 public function getName()
 {
 return 'Post';
 }

}
									

ThreadType.php

<?php
// CodeConsortium/ForumBundle/Form/Type/ThreadType.php
namespace CodeConsortiumForumBundleFormType;

use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilder;

class ThreadType extends AbstractType
{

 private $options;

 public function __construct(array $options = null)
 {
 $this->options = $options;
 }

 public function buildForm(FormBuilder $builder, array $options)
 {
 $builder->add('board', new BoardType($this->options) );

 if ($this->options['thread']['show_title'])
 $builder->add('title');

 }

 // for creating and replying to threads
 public function getDefaultOptions(array $options)
 {
 return array(
 'data_class' => 'CodeConsortiumForumBundleEntityThread',
 );
 }

 public function getName()
 {
 return 'Thread';
 }

}
									

And lastly, the BoardType.php

<?php
// CodeConsortium/ForumBundle/Form/Type/BoardType.php
namespace CodeConsortiumForumBundleFormType;

use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilder;

class BoardType extends AbstractType
{

 private $options;

 public function __construct(array $options = null)
 {
 $this->options = $options;
 }

 public function buildForm(FormBuilder $builder, array $options)
 {

 }

 // for creating and replying to threads
 public function getDefaultOptions(array $options)
 {
 return array(
 'data_class' => 'CodeConsortiumForumBundleEntityBoard',
 );
 }

 public function getName()
 {
 return 'Board';
 }

}
									

Notice how i added a constructor, this is so we can easily chain feed in from the first object any changes to the form, such as wether we wish to bother including a field or not, we can opt to have any field removed if the form is used under different circumstances such as different access controls for different users with different permissions or if the form is used in another way on the site.

One more thing we need to make sure, is that all the references in our entities make use of the cascade={“persist”} option so that it maintains relations between the relevant forms as new ones get processed, so if say a thread does not exist then when processing the post, it should create one and do the association for us.

Here is an example of a snip from my Post entity class Post.php

 /**
 * @ORMManyToOne(targetEntity="Thread", inversedBy="posts", cascade={"persist"})
 * @ORMJoinColumn(name="thread_id", referencedColumnName="id")
 */
 protected $thread;
									

Lastly, you will need a formHandler if your not working in your controller, if using my example code then the postFormHandler.php looks like this:

<?php
// CodeConsortium/ForumBundle/Form/Handler/PostFormHandler.php
namespace CodeConsortiumForumBundleFormHandler;

use SymfonyComponentFormForm;
use SymfonyComponentHttpFoundationRequest;

use CodeConsortiumForumBundleFormTypePostType;
use CodeConsortiumForumBundleEntityPost;

class PostFormHandler
{

 protected $request;
 protected $form;
 protected $post;

 public function __construct(Form $form, Request $request, $post) //, $entityManager)
 {
 $this->form = $form;
 $this->request = $request;
 $this->post = $post;
 }

 public function process()
 {
 //$form = $this->createForm(new PostType(), $this->post);
 //$this->form->setData($this->post);

 if ($this->request->getMethod() == 'POST')
 {
 $this->form->bindRequest($this->request);

 if ($this->form->isValid())
 { 

 return true; 
 }

 }

 return false;
 }

}
									

Sep19

‘cd ..’, ‘cd ./‘ when updating directories in Max OS X.

So a strange thing happened while doing some coding in symfony 2. At first i thought it was symfony 2, but realised i was using the wrong version, so no problem. I got the latest version, renamed the old one by appending an underscore and put the updated one in the same place with the name of the old directory.

So ‘symfony’ became ‘symfony_’ then the new folder could be called ‘symfony’ without getting an error about trying to name a directory the same as an existing one. This part was done in the finder.

No problem’o, now when i try to use the command line, which might i mention was already being used prior to shuffling the directories and i try to do ‘pwd’ it tells me i am in ‘symfony’. Great, however i still get the same errors as before, which i thought odd. When i did ‘cd ..’ then ‘cd symfony’, it fixed it, even though prior to that ‘pwd’ told me i was in the symfony folder, in reality, the command line thought i was in the now defunct directory ‘symfony_’.

So it seems when renaming a directory to put another of the same name in its place, if the command line was already ‘cd’d’ in there, then it will still point to the old directory regardless of the change of name. And to resolve this, you need to cd back then forward again.

I am just guessing here, but perhaps the cmd points to nodes on the filesystem and thusly does not care much about the directory name.

Not sure if this is a mac issue or a inherent problem with the unix/posix standard. Interesting and annoying though non-the-less.

Jul13

Using WebView in XCode 4.0 with Objective-C

If you are wanting to use the WebView in your XCode/Obj-C project and are a little underwhelmed and confused by Apple’s docs, as i initially was then here i can show you step by step how to get up and running very quickly and very easily.

Firstly, before we begin this little tutorial is from what i have worked out myself, i found the documentation a bit confusing myself, it tends to ramble on a lot and not give the reader much in the way of code examples as i will do here; namely it talks more about Carbon than Cocoa front ends and the example code is very obscure and out of context and fails to mention several key steps for newcomers to Cocoa/Obj-C. The following steps are what i gathered from the documentation plus a little googling.

Chances are, if your reading this then like myself you got an error when trying to run you app after placement of the WebView and further more if you get past compilation you get a runtime error of: ”program received signal: SIGABRT” in the code as a sort of fix-it tab and a GDB output of: “Terminating app due to uncaught exception ‘NSInvalidUnarchiveOperationException’, reason: ‘*** -[NSKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (WebView)”.

The 2 most significant steps that the apple docs don’t mention are 1) stating the header file for Webkit, which if your used to working in Windows you would not import the rendering engine for a webview equivalent and may be thrown by that and further more 2) import the webkit framework into the project.

So heres the steps you should take:

1) Create your project, here i created a basic cocoa project called webber.

2) Add the WebKit.framework to your project as shown below:

On the project configuration page click the plus under the "Linked Framework and Libraries" list as shown in the figure above.

search for webkit and select the webkit framework.

The linked frameworks and libraries should look like the figure shown above.

3) Add the webView to your window, in this instance i also added a textbox and a go button:

Add your webView to your project.

4) Drag your IBOutlet connections to your AppDelegate, you will need an outlet for the navigation textbox (navbar) and one for the webView component (webber), and lastly a IBAction for the go button (go). Then at the top of the code add a header for webkit, #import <Webkit/Webkit.h>. You should have something resembling this:

Establish your IBOutlets and IBActions as shown above and add the Webkit header.

5) Now we add the implementation code to your AppDelegate.m file in the go and applicationDidFinishLaunching method:

Add the implementation code.

So the first applicationDidFinishLaunching method will set the homepage of your webView to the apple site initially and after that you can type in a url in the textbox and click go and the browser will take you to the requested site.

Simples! I hope this helped those of you who bothered to read. Good luck and have fun with it.

 

Apr06