December 23, 2009 New Testing IE 7 AND IE 8 with IETester
Recently I’ve been having some problems with IE. They are usually with CSS layouts and javascript which work in IE 8 and not in IE 7.
I found a nice application which runs the IE rendering and javascript engines from IE 5.5, 6, 7 and 8. It crashes a lot, but it allows me to easily view the same pages across multiple versions of IE from the same computer.
The link is:
http://my-debugbar.com/wiki/IETester/HomePage
You’ll also need to download their “debug bar” in order to view the source from within the application. I thought it was a bit odd that this was separated from the main application.
Happy debugging!
- Leave a comment
- Posted under CSS, Design, Javascript
November 29, 2009 Symfony prints your titles twice
I recently received a request from an SEO / online marketing company to remove the extra title from one of our sites. Symfony was displaying the title information for every page twice; once in the title tag, and again in as a meta tag with name=”title”.
Google was not displaying the title they had given the index page, and there was a concern that Google considered the title duplication to be spam.
Both the title tag and the meta tags are generated from the helper functions include_title and include_metas, respectively. I could find no easy, configuration-based way to tell include_metas to not include the title meta, so I decided replace the helper with the following:
<?php
foreach ( sfContext::getInstance()->getResponse()->getMetas() as $name => $content ):
if ( $name != 'title' ):
echo tag('meta', array('name' => $name, 'content' => $content))."\n";
endif;
endforeach;
?>
About a month later there is no change in how Google displays the site’s index page. Perhaps it will take longer to find out if this will change it. Or if it won’t change anything.
Have you had any trouble with Google not displaying your page titles in their results? Do you know if displaying the same title twice, both in the title tag and in the meta title tag, is affecting things?
Please do comment.
- Leave a comment
- Posted under SEO, Symfony 1.0
September 30, 2009 Giving your symfony apps better urls
I love pretty URLs. I don’t like file name extensions unless they end with .html.
All versions of symfony save me from address bar ugliness, but only for the first application I create. Everything else is ass.php/backwards.
I’m going to give you two ways of beautifying your second, third, and forth applications so you can http://www.get.your/application/looking/sexy. Both methods involve creating a subdirectory.
Put it all in a subdirectory, and symlink your way out
So if you have an app named “admin,” you can do the following:
- Create a directory in your web folder called admin
- Copy your admin.php and admin_dev.php into this folder
- Copy the .htaccess file into this folder
- Rename admin.php to index.php
- Edit the file now named index.php, and change the require_once statement at the top so it’s an extra directory back
- Symbolically link the js, css, images, and uploads directories from within the admin directory to the directory above; so from your app directory:
ln -s ../js
This is what I usually do in a Linux environment. However, I’m developing on a Windows box, and symbolic links don’t translate easily between Windows and Linux. So here’s the second option, without using symbolic links:
Put just the .htaccess in a subdirectory
- As before, create a subdirectory in your web folder for the admin.
- Copy the .htaccess file in, and at the very bottom of the file, change index.php to ../admin.php. It should read
RewriteRule ^(.*)$ ../admin.php [QSA,L] - Edit your routing.yml file for the admin app, and append /admin/ to all of your routes.
That should about cover it. Also remember to set no_script_name to on in each of your applications.
Enjoy.
Alternative methods? Please leave comments.
- 4 comments
- Posted under Setup, Symfony 1.0, Symfony 1.2
September 29, 2009 Disabling the submit button with jQuery to prevent extra submissions
It happens.
Not often, but once in a while a user submits the same thing. Twice, maybe three times, and each just a second or two apart.
It’s easy to understand why. They click the submit button and then … nothing happens. If it doesn’t work right away, we assume it’s not working. The solution? Hit the button again! Hit it five times until finally, it seems to work.
There are two things you need to do to stop this from happening.
- Assure the user they successfully clicked the button, and they only have to wait a moment for the form to be processed.
- Stop the user from hitting the button again.
I’m a jQuery newbie, but it wasn’t long before I was in love with this javascript library. Here is my solution:
- Download the jQuery library if you haven’t already, and drop it in your js directory if you’re using Symfony.
- Reference the jquery library in your template, like so:
<?php use_javascript('jquery') ?> - And finally, here’s the source code:
<script type="text/javascript">
$(document).ready(function(){
$("input[type='submit']").attr("disabled", false);
$("form").submit(function(){
$("input[type='submit']").attr("disabled", true).val("Please wait...");
return true;
})
})
</script>
I love the simplicity.
Once the document is ready, make sure every submit button on the page is enabled. In Firefox 3.5, if I click the back button to go back to the form after submitting, the submit button stays disabled. I’m not sure why; the value attribute is correct, but it stays disabled, even if you refresh.
In the next few statements, I add an onsubmit event to the form. If you have javascript validation on your form, you’ll have to execute your validation from within this function.
The function finds all submit buttons within the form, disables them, and changes their value to “Please wait…”. All of this is done dynamically, without altering the form directly in my html. Love it.
Does anyone have any server-side solutions to this problem? If you do I’d love to hear about them.
- 4 comments
- Posted under jQuery
September 20, 2009 Country codes in YAML format
Here are all the country codes in YAML format for your downloading pleasure! You may place them in your app.yml (for symfony users) and call the list with sfConfig::get(‘app_countries’).
all:
.lists:
countries:
CA: Canada
US: United States of America
AD: Andorra
AE: United Arab Emirates
AF: Afghanistan
AG: Antigua & Barbuda
AI: Anguilla
AL: Albania
AM: Armenia
AN: Netherlands Antilles
AO: Angola
AQ: Antarctica
AR: Argentina
AS: American Samoa
AT: Austria
AU: Australia
AW: Aruba
AZ: Azerbaijan
BA: Bosnia and Herzegovina
BB: Barbados
BD: Bangladesh
BE: Belgium
BF: Burkina Faso
BG: Bulgaria
BH: Bahrain
BI: Burundi
BJ: Benin
BM: Bermuda
BN: Brunei Darussalam
BO: Bolivia
BR: Brazil
BS: Bahama
BT: Bhutan
BU: Burma (no longer exists)
BV: Bouvet Island
BW: Botswana
BY: Belarus
BZ: Belize
CC: Cocos (Keeling) Islands
CF: Central African Republic
CG: Congo
CH: Switzerland
CI: Côte D'ivoire (Ivory Coast)
CK: Cook Iislands
CL: Chile
CM: Cameroon
CN: China
CO: Colombia
CR: Costa Rica
CS: Czechoslovakia (no longer exists)
CU: Cuba
CV: Cape Verde
CX: Christmas Island
CY: Cyprus
CZ: Czech Republic
DD: German Democratic Republic (no longer exists)
DE: Germany
DJ: Djibouti
DK: Denmark
DM: Dominica
DO: Dominican Republic
DZ: Algeria
EC: Ecuador
EE: Estonia
EG: Egypt
EH: Western Sahara
ER: Eritrea
ES: Spain
ET: Ethiopia
FI: Finland
FJ: Fiji
FK: Falkland Islands (Malvinas)
FM: Micronesia
FO: Faroe Islands
FR: France
FX: France, Metropolitan
GA: Gabon
GB: United Kingdom (Great Britain)
GD: Grenada
GE: Georgia
GF: French Guiana
GH: Ghana
GI: Gibraltar
GL: Greenland
GM: Gambia
GN: Guinea
GP: Guadeloupe
GQ: Equatorial Guinea
GR: Greece
GS: South Georgia and the South Sandwich Islands
GT: Guatemala
GU: Guam
GW: Guinea-Bissau
GY: Guyana
HK: Hong Kong
HM: Heard & McDonald Islands
HN: Honduras
HR: Croatia
HT: Haiti
HU: Hungary
ID: Indonesia
IE: Ireland
IL: Israel
IN: India
IO: British Indian Ocean Territory
IQ: Iraq
IR: Islamic Republic of Iran
IS: Iceland
IT: Italy
JM: Jamaica
JO: Jordan
JP: Japan
KE: Kenya
KG: Kyrgyzstan
KH: Cambodia
KI: Kiribati
KM: Comoros
KN: St. Kitts and Nevis
KP: Korea, Democratic People's Republic of
KR: Korea, Republic of
KW: Kuwait
KY: Cayman Islands
KZ: Kazakhstan
LA: Lao People's Democratic Republic
LB: Lebanon
LC: Saint Lucia
LI: Liechtenstein
LK: Sri Lanka
LR: Liberia
LS: Lesotho
LT: Lithuania
LU: Luxembourg
LV: Latvia
LY: Libyan Arab Jamahiriya
MA: Morocco
MC: Monaco
MD: Moldova, Republic of
MG: Madagascar
MH: Marshall Islands
ML: Mali
MN: Mongolia
MM: Myanmar
MO: Macau
MP: Northern Mariana Islands
MQ: Martinique
MR: Mauritania
MS: Monserrat
MT: Malta
MU: Mauritius
MV: Maldives
MW: Malawi
MX: Mexico
MY: Malaysia
MZ: Mozambique
NA: Namibia
NC: New Caledonia
NE: Niger
NF: Norfolk Island
NG: Nigeria
NI: Nicaragua
NL: Netherlands
"NO": Norway
NP: Nepal
NR: Nauru
NT: Neutral Zone (no longer exists)
NU: Niue
NZ: New Zealand
OM: Oman
PA: Panama
PE: Peru
PF: French Polynesia
PG: Papua New Guinea
PH: Philippines
PK: Pakistan
PL: Poland
PM: St. Pierre & Miquelon
PN: Pitcairn
PR: Puerto Rico
PT: Portugal
PW: Palau
PY: Paraguay
QA: Qatar
RE: Réunion
RO: Romania
RU: Russian Federation
RW: Rwanda
SA: Saudi Arabia
SB: Solomon Islands
SC: Seychelles
SD: Sudan
SE: Sweden
SG: Singapore
SH: St. Helena
SI: Slovenia
SJ: Svalbard & Jan Mayen Islands
SK: Slovakia
SL: Sierra Leone
SM: San Marino
SN: Senegal
SO: Somalia
SR: Suriname
ST: Sao Tome & Principe
SU: Union of Soviet Socialist Republics (no longer exists)
SV: El Salvador
SY: Syrian Arab Republic
SZ: Swaziland
TC: Turks & Caicos Islands
TD: Chad
TF: French Southern Territories
TG: Togo
TH: Thailand
TJ: Tajikistan
TK: Tokelau
TM: Turkmenistan
TN: Tunisia
TO: Tonga
TP: East Timor
TR: Turkey
TT: Trinidad & Tobago
TV: Tuvalu
TW: Taiwan, Province of China
TZ: Tanzania, United Republic of
UA: Ukraine
UG: Uganda
UM: United States Minor Outlying Islands
UY: Uruguay
UZ: Uzbekistan
VA: Vatican City State (Holy See)
VC: St. Vincent & the Grenadines
VE: Venezuela
VG: British Virgin Islands
VI: United States Virgin Islands
VN: Viet Nam
VU: Vanuatu
WF: Wallis & Futuna Islands
WS: Samoa
YD: Democratic Yemen (no longer exists)
YE: Yemen
YT: Mayotte
YU: Yugoslavia
ZA: South Africa
ZM: Zambia
ZR: Zaire
ZW: Zimbabwe
ZZ: Unknown or unspecified country
Tags: country codes, symfony, YAML
- 4 comments
- Posted under Symfony 1.2, YAML
August 17, 2009 Securing multi-user CRUD operations through the myUser class in Symfony
Symfony has automated the process of creating a CRUD application, and even has built-in CSRF protection.
However, for a site with multiple users, the generated CRUD code just won’t do.
In my schema, each row of a model is owned by a user, according to their user_id. In the dashboard application I am building, each user has a list of programs.
They can create, read, update and delete each program that they own.
But the generated actions would allow users access to other user’s programs, simply by changing the id in URLs such as /program/edit/id/1.
My solution? Store the user_id of the currently logged in user in myUser.class.php. Since the myUser class has ownership of the id of the currently logged in user, I thought it was appropriate to implement convenience getters and setters for the dashboard here.
So my list action would contain:
$this->programs = $this->getUser()->getPrograms();
The myUser::getPrograms() method is implemented like so:
public function getPrograms()
{
return Doctrine::getTable('Program')->fetchByOwner($this->getId());
}
public function getProgram($pk)
{
$program = Doctrine::getTable('Program')->find($pk);
return $this->hasOwnershipOf($program) ? $program : false;
}
public function hasOwnershipOf($row)
{
if ( isset($row['user_id']) )
{
if ( $row['user_id'] == $this->getId() )
{
return true;
}
}
return false;
}
Adding the myUser::fetchByOwner() method leaves the model functionality open to being called from another administrative application other than the dashboard. You could use a Doctrine magic method here, however I would prefer to return programs in alphabetical order (or perhaps allow for other options to be passed in using a Doctrine_Query as a second parameter).
For myUser::getProgram($pk), I simply use the ::find() magic method in doctrine to retrieve the object by primary key, as usual, and then return it only if it matches the user_id of the logged-in user.
Writing for multiple users is common place for web-based applications. However, it’s something I don’t usually deal with at work where I write simple, single-user back ends for editing website content.
Is there a more elegant way of handling user ownership of database content? Perhaps by writing a doctrine behavior? How do you usually go about it?
Please comment.
- 3 comments
- Posted under Doctrine, PHP, Symfony 1.2
August 12, 2009 Create a dynamic CSS module in Symfony 1.2 fast
I’m sure there are a number of ways you can handle this. There are also tons of modifications you can do to make it more useful, or better adapted to your particular needs. This post will quickly illustrate one way to create a dynamic stylesheet in Symfony 1.2.
I just finished writing a module that allows my users to change their background color. So I thought I’d share a similar way of doing this with an example.
Create a new doctrine model. Since we’re keeping things simple, our style sheet will only have one editable element: the body tag’s background-image.
DynamicCSS:
columns:
identifier: string(255)
background_color: string(255)
Add a fixture and load it up:
/data/fixtures/fixtures.yml
DynamicCSS:
black:
identifier: "main"
background_color: "#000000"
$ symfony doctrine:build-all-reload
The identifier will be the stylesheet’s name. You’ll see it in the route, which you’ll add now:
/apps/frontend/config/routing.yml
dynamiccss:
url: /dynamiccss/:identifier.css
param: { module: dynamiccss, action: render }
Create a new module:
$ symfony init-module frontend dynamiccss
This module will be used to render a dynamic style sheet. You’ll want to make sure it doesn’t render the layout, and that it returns a content type of ‘text/css’. So edit the actions file and add a preExecute() method. Of course, you’ll also need a method to fetch the style sheet and render it.
/apps/frontend/modules/dynamiccss/actions/actions.class.php
...
public function preExecute()
{
$this->setLayout(false);
$this->getResponse()->setContentType('text/css');
}
public function executeRender()
{
$this->stylesheet = Doctrine::getTable('DynamicCSS')
->getByIdentifier($request->getParameter('identifier'));
$this->forward404Unless($this->stylesheet);
}
...
Next we’ll add the template, which is at this point is very simplistic. You’ll need to have that ::getByIdentifier() method implemented in the DynamicCSSTable class, as well.
/apps/frontend/modules/dynamiccss/templates/renderSuccess.php
body {
background-color: <?php echo $stylesheet->getBackgroundColor() ?>;
}
/lib/model/doctrine/DynamicCSSTable.class.php
...
public static function getByIdentifier($identifier)
{
return $this->createQuery('c')
->addWhere('c.identifier = ?', $identifier)->fetchOne();
}
...
Now you can add your dynamic style sheet to the view. Here is an example:
/apps/frontend/config/view.yml
default:
...
stylesheets: [/dynamiccss/main.css: { raw_name: true }]
...
That “raw_name” option allows you to override the typical way of creating an asset’s file name.
Now whenever someone edits the background color for the “main” style sheet, from the database, the background color can change.
You’ll still need to add an interface for editing the background color (or however many other options you add). That is up to you.
Complaints? Pointers? Thought of a better way?
Please post.
Tags: dynamic css, Symfony 1.2
- 5 comments
- Posted under CSS, Design, Doctrine, PHP, Symfony 1.2
July 22, 2009 Table-level behaviors will remove global behaviors in Doctrine schemas
Here is something to watch out for. If you define global behaviors in your schema, and then define table-level behaviors, the global behavior will be removed (unless you declare it again on the table level).
Here is a quick example:
actAs: [Timestampable]
Program:
actAs:
Sluggable:
fields: [name]
columns:
name: string(255)
user_id: integer(4)
relations:
sfGuardUser:
foreignType: many
type: one
local: user_id
foreign: id
For a short while I couldn’t figure out why the created_at and updated_at fields weren’t showing up in the Program table. Then I realized that it was removed at the table level, and I had to add it in again.
The official documentation does not mention this:
http://www.symfony-project.org/doctrine/1_2/en/04-Schema-Files#chapter_04_global_schema_information
Hopefully this will save you 15 minutes of scratching your head.
Here is more information on Symfony 1.2 and doctrine:
http://www.symfony-project.org/doctrine/1_2/en/04-Schema-Files
- Leave a comment
- Posted under Behaviors, Doctrine, Symfony 1.2
July 18, 2009 Using Symfony filters and CSS to present a design in-context
This post assumes you have some experience with Symfony 1.0.
At my work I needed to create several different versions of the same site. This mainly entailed changing background images to get a slightly different look and feel, and changing the content category, depending on a request parameter.
For this tutorial, I will use the company I work for as an example. Momentum IT Group has two distinct divisions: ELearning and Web. So let’s say I wanted to make slight design changes depending on which division I’m looking at.
We’ll start where all things Symfony begin: with the routes. Open up apps/frontend/config/routing.yml and enter the following:
division_page:
url: /:division/:slug
param: { module: cms, action: display }
requirements: { division: elearning|web }
I leave the cms module for you to implement. The display action would take a unique slug as a key to look up the page content from the database for display.
In this route, the division is required to be either elearning or web, and these two options will be used to display a different background design (and possibly select from a different content category in your cms module).
The next step is to implement the switch which determines which background to apply, if any. You can always write this code in your cms module, but you may have other modules other than the cms module which need to change design elements depending on which division the user is viewing. To handle this situation, you can create a filter.
A filter is applied once for each request. Start by opening apps/frontend/config/filters.yml and adding a new filter class:
rendering: ~ web_debug: ~ security: ~ # generally, you will want to insert your own filters here cssFilter: class: cssFilter cache: ~ common: ~ flash: ~ execution: ~
Create the filter class in apps/frontend/lib/cssFilter.class.php:
<?php
class cssFilter extends sfFilter
{
public function execute($filterChain)
{
if ( $this->isFirstCall())
{
$response = $this->getContext()->getResponse();
switch ( sfContext::getInstance()->getRequest()->getParameter('division') )
{
case 'elearning':
$response->addStylesheet('elearning', 'last');
break;
case 'web':
$response->addStylesheet('web', 'last');
break;
}
}
}
}
Make sure you place two new CSS files in your web/css directory.
For this example, they are elearning.css and web.css. They will be the last css files in your HTML header and will override your main.css where needed. So, you might override a background image like so:
web/css/main.css
body {
background: url(../images/background.jpg) no-repeat top left;
}
web/css/elearning.css
body {
background-image: url(../images/elearning.jpg);
}
Now if you direct your browser to [your_site]/elearning/[page_slug], you will have taken advantage of CSS cascading and Symfony filters to present a slightly different design depending on the context.
