Granting a user read-only access on [dbo] but full access on their own schema
The correct grants are something like:

create user joe without login
go
create schema joe_schema
go

grant select on schema::dbo to joe;
alter authorization on schema::joe_schema to joe;

grant create table, 
      create procedure, 
      create view, 
      create function, 
      create type 
      to joe;


It's pretty important that you don't grant the user the ability to alter a schema owned by another user, especially one owned by dbo.  If you do ownership chains will allow the user to skip permissions checks on the schema owner's objects in all schemas.