tag:blogger.com,1999:blog-52575557361067627012024-02-15T05:46:51.007-08:00Learn YiiQuick snippets to get you up to speed with the Yii PHP framework. By Neil McGuigan.Neil McGuiganhttp://www.blogger.com/profile/14122981831780837323noreply@blogger.comBlogger53125tag:blogger.com,1999:blog-5257555736106762701.post-76830882215184935982012-07-10T12:12:00.001-07:002012-07-10T12:12:58.292-07:00How to make a symlink in Windows 7<br />
<ol>
<li>open cmd (as an admin)</li>
<li>type:</li>
</ol>
<pre>
mklink /d linkpath targetpath
</pre>
<div>
<br /></div>
<div>
example:</div>
<div>
<br /></div>
<pre>
mklink /d c:\easytoremember c:\users\neil\local\applicationdata\temp\hardtoremember
</pre>Neil McGuiganhttp://www.blogger.com/profile/14122981831780837323noreply@blogger.com2tag:blogger.com,1999:blog-5257555736106762701.post-72348275884288813012012-04-16T22:23:00.002-07:002012-04-16T22:23:55.604-07:00on Database TreesThis is more a note to myself, but it looks like the best tree option in a database is Recursive Query, at least in Postgres, which supports the WITH clause. <br />
<br />
Closure Table is the next best option.Neil McGuiganhttp://www.blogger.com/profile/14122981831780837323noreply@blogger.com1tag:blogger.com,1999:blog-5257555736106762701.post-88987086159259331232012-04-15T18:14:00.002-07:002012-04-15T18:16:31.757-07:00Optimistic Concurrency Control with Yii's ActiverecordHere's how to do <a href="http://en.wikipedia.org/wiki/Optimistic_concurrency_control">Optimistic Concurrency Control</a> with Yii's ActiveRecord:<br />
<pre>class OCCActiveRecord extends CActiveRecord{
public $checksum = null;
public function afterFind(){
/* get a checksum of all the attribute values after reading a record from db */
$this->checksum = md5(implode('', $this->getAttributes(false)));
parent::afterFind();
}
public function rules(){
/* use an exist validator to make sure that a record with the same checksum is still in db. if it's been modified, then checksums will be different */
return array(
array('id', 'exist', 'message'=>'This record was modified after you read it', 'on'=>'update', 'criteria'=>array('condition'=>'md5(concat('.implode(',',$this->attributeNames()).'))=:checksum', 'params'=>array('checksum'=>$this->checksum)))
);
}
}
</pre>Just a rough sketch, but you get the idea. <br />
<br />
You could also modify this to be a behavior. Works only with MySQL AFAIK.Neil McGuiganhttp://www.blogger.com/profile/14122981831780837323noreply@blogger.com4tag:blogger.com,1999:blog-5257555736106762701.post-17553243941303286012012-04-15T18:01:00.003-07:002012-04-15T18:03:01.032-07:00Table Inheritance with Yii ActiveRecordNeed to use Table Inheritance with ActiveRecord? Here's how:<br />
<br />
This is <a href="http://martinfowler.com/eaaCatalog/singleTableInheritance.html">Single Table Inheritance only</a>.<br />
<br />
Imagine that you want People contacts and Organization contacts. You don't want to keep them in different tables, as sometimes you sell to people, and sometimes you sell to organizations, and you want to keep a single (foreign) key to the party that you sold to.<br />
<br />
We will create a class called Party, with subtypes Person and Organization. They will share some of Party's rules and relationships.<br />
<br />
Here is the database schema:<br />
<br />
<pre>CREATE TABLE `tbl_party` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`type` char(1) NOT NULL comment 'p=person,o=organization',
`name` varchar(255) NOT NULL comment 'surname if a person',
`given_name` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ;
</pre><br />
and here is the model code:<br />
<br />
<pre>class Party extends CActiveRecord {
public static function model($className=__CLASS__){
return parent::model($className);
}
public function tableName(){
return 'tbl_party';
}
public function relations(){
return array(
);
}
public function rules(){
return array(
array('name,type','required'),
array('type','in', 'range'=>array('o','p'))
);
}
}
class Person extends Party {
/* this is required for subtypes */
public static function model($className=__CLASS__){
return parent::model($className);
}
public function defaultScope(){
/* only read Parties that are People */
return array(
'condition'=>"type='p'"
);
}
public function relations(){
/* combine parent and own relations. can have 'People' only relations */
$parentRelations = parent::relations();
$myRelations = array(
);
return array_merge($myRelations, $parentRelations);
}
public function rules(){
/* combine parent and own rules */
$parentRules = parent::rules();
$myRules = array(
array('type', 'default', 'value'=>'p'), /* set type to Person */
array('type', 'in', 'range'=>array('p')), /* allow only Person type */
array('given_name', 'required') /* new rule for this subtype only */
);
/* you want to apply parent rules last, delete them here if necessary */
return array_merge($myRules, $parentRules);
}
}
class Organization extends Party {
public static function model($className=__CLASS__){
return parent::model($className);
}
public function defaultScope(){
return array(
'condition'=>"type='o'"
);
}
public function relations(){
$parentRelations = parent::relations();
$myRelations = array(
);
return array_merge($myRelations, $parentRelations);
}
public function rules(){
$parentRules = parent::rules();
$myRules = array(
array('type', 'default', 'value'=>'o'),
array('type', 'in', 'range'=>array('o'))
);
/* you want to apply parent rules last. delete them if necessary */
return array_merge($myRules, $parentRules);
}
}
</pre>Neil McGuiganhttp://www.blogger.com/profile/14122981831780837323noreply@blogger.com5tag:blogger.com,1999:blog-5257555736106762701.post-74769324543762048222012-02-09T11:16:00.000-08:002012-07-11T11:12:29.276-07:00Database Notes<div>
Wanna build your schema online and share?</div>
<div>
<br /></div>
<div>
<a href="http://ondras.zarovi.cz/sql/demo/?keyword=default">WW SQL Designer</a> </div>
<div>
<br /></div>
<div>
Wanna test your SQL query online and share?</div>
<div>
<br /></div>
<div>
<a href="http://sqlfiddle.com/">SQL Fiddle</a> </div>
<div>
<br /></div>
<div>
This might come in handy for you, if you are struggling with a SQL query:</div>
<div>
<br /></div>
<div>
<a href="http://www.artfulsoftware.com/infotree/queries.php">Common MySQL Queries</a> </div>
<div>
<br /></div>
<div>
If you don't fully understand JOINs:</div>
<div>
<br /></div>
<div>
<a href="http://www.codinghorror.com/blog/2007/10/a-visual-explanation-of-sql-joins.html">A Visual Explanation of SQL Joins</a></div>
<div>
<br /></div>
<div>
Wanna hot-backup your InnoDB database?</div>
<div>
<br /></div>
<div>
<a href="http://www.percona.com/software/percona-xtrabackup/">Percona XtraBackup</a> (open source)</div>
<div>
<br /></div>
<div>
Need to recover a corrupt InnoDB table?</div>
<div>
<br /></div>
<div>
<a href="http://www.percona.com/software/mysql-innodb-data-recovery-tools/">Percona Data Recovery Tool for InnoDB</a> (open source)</div>
<div>
<br /></div>
<div>
Need to generate massive test data?</div>
<div>
<br /></div>
<div>
<a href="http://databene.org/benerator">Benerator</a> <br />
<br />
Want to Validate your SQL?<br />
<br />
<a href="http://developer.mimer.se/validator/parser92/index.tml">Mimer SQL Validator</a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
Random:</div>
<div>
<ol>
<li>if you want decent full-text search, choose PostgreSQL over MySQL, as MySQL doesn't support free-text search at the same time as foreign keys and transactions.</li>
<li>Set your database to use the UTC timezone</li>
</ol>
</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>Neil McGuiganhttp://www.blogger.com/profile/14122981831780837323noreply@blogger.com0tag:blogger.com,1999:blog-5257555736106762701.post-83646110971935753412011-11-23T15:45:00.000-08:002011-11-23T15:47:48.288-08:00How to read a JSON POST with Yii, and save it to the databaseLet's say you are sending a json-encoded object to your create action, and want to save it in your database. here's how:<br />
<br />
<pre class="">public function actionCreate() {</pre><pre class=""> </pre><pre class="">//read the post input (use this technique if you have no post variable name):
$post = file_get_contents("php://input");
//decode json post input as php array:
$data = CJSON::decode($post, true);
//contact is a Yii model:
$contact = new Contact();
//load json data into model:
$contact->attributes = $data;
</pre><pre class=""></pre><pre class="">//this is for responding to the client:
$response = array();
//save model, if that fails, get its validation errors:
if ($contact->save() === false) {
$response['success'] = false;
$response['errors'] = $contact->errors;
} else {
$response['success'] = true;
//respond with the saved contact in case the model/db changed any values
$response['contacts'] = $contact;
}
//respond with json content type:
header('Content-type:application/json');
</pre><pre class="">//encode the response as json:
echo CJSON::encode($response);
//use exit() if in debug mode and don't want to return debug output
exit();
}
</pre>Neil McGuiganhttp://www.blogger.com/profile/14122981831780837323noreply@blogger.com5tag:blogger.com,1999:blog-5257555736106762701.post-67276128909000985862011-11-04T14:37:00.001-07:002011-11-04T14:42:18.920-07:00Here is my new blog about learning Ext JSExtJS is a great rich internet application library, written in javascript<br />
<br />
<a href="http://extjs-tutorials.blogspot.com/">http://extjs-tutorials.blogspot.com/</a>Neil McGuiganhttp://www.blogger.com/profile/14122981831780837323noreply@blogger.com0tag:blogger.com,1999:blog-5257555736106762701.post-85196014295352296132011-10-28T10:08:00.001-07:002011-10-28T10:08:57.019-07:00Abandoning Jquery and Server-Generated HTML, moving to ExtJS + Yii backendOur team wasted far too long trying to get our web app working with jQuery. Finally we discovered the joys of ExtJS 4, and are moving all development there.<br />
<br />
Still using Yii as the backend for its nice models and controllers, as well as RBAC.Neil McGuiganhttp://www.blogger.com/profile/14122981831780837323noreply@blogger.com15tag:blogger.com,1999:blog-5257555736106762701.post-78407115787741204472011-08-20T14:04:00.000-07:002011-08-28T21:09:13.078-07:00Which databases does Yii support?Yii uses PDO, which is good, as it supports parameters, which can help to prevent SQL injection attacks.<br />
<br />
As of Yii 1.1.8, it supports the following:<br />
<br />
<ul><li>PostgreSQL</li>
<li>MySQL</li>
<li>sqlite 3 and sqlite 2</li>
<li>Microsoft SQL Server</li>
<li>Oracle</li>
</ul><div>Yii also supports <a href="http://dblib.sourceforge.net/">dblib</a></div><div><br />
</div><div>Some other useful database features supported by Yii include:</div><div><ul><li>transactions</li>
<li>schema caching for ActiveRecord</li>
<li>query caching</li>
<li>null conversion</li>
<li>database cache dependencies</li>
<li>performance profiling</li>
</ul></div>Neil McGuiganhttp://www.blogger.com/profile/14122981831780837323noreply@blogger.com1tag:blogger.com,1999:blog-5257555736106762701.post-83831695126424485422011-08-10T15:52:00.000-07:002011-08-10T15:52:30.620-07:00Use session instead of stateful formsHere's why:<br />
<br />
<br />
<ol><li>If you decide to use AJAX later on (in particular I mean to <a href="http://en.wikipedia.org/wiki/Hijax">HIJAX</a> your forms), you'll have to set and get the YII_PAGE_STATE variable for every action, which is a pain in the ass. </li>
<li>page state is broken when you try to use partial rendering. Even if the partially rendered form is a complete CActiveForm, YII_PAGE_STATE will end up blank. </li>
</ol><div>I am referring to pages that post back to themselves and that have related sub-items for which you need to maintain state. </div>Neil McGuiganhttp://www.blogger.com/profile/14122981831780837323noreply@blogger.com0tag:blogger.com,1999:blog-5257555736106762701.post-4454877817461200552011-08-06T16:19:00.000-07:002012-05-05T14:08:47.406-07:00How to install phpunit on Windowsthe PHPUnit installer appears to be written by someone with mild autism....there's no other explanation for it.<br />
<br />
either way, here's how i was able to install phpunit:<br />
<br />
<br />
open the command prompt, type: (enter for each line)<br /><ol>
<li>pear channel-update pear.php.net</li>
<li>pear upgrade-all</li>
<li>pear channel-discover pear.phpunit.de</li>
<li>pear channel-discover components.ez.no</li>
<li>pear channel-discover pear.symfony-project.com</li>
<li>pear update-channels</li>
<li>pear install -a -f phpunit/PHPUnit</li>
</ol>
<div>
<br /></div>Neil McGuiganhttp://www.blogger.com/profile/14122981831780837323noreply@blogger.com8tag:blogger.com,1999:blog-5257555736106762701.post-14494243598316414202011-07-18T13:54:00.000-07:002011-07-18T20:59:57.204-07:00How to output related values in jsonCJSON::encode() is nice for outputting arrays of models or a dataProvider, but it doesnt output relations. <br />
<br />
here's how to do it. This function will output an array of models, with their related values (as deep as you wanna go), in JSON format. Instead of showing every attribute, you indicate the ones you want to output using $attributeNames. The key is the <a href="http://www.yiiframework.com/doc/api/1.1/CHtml#value-detail">CHtml::value()</a> function, that can output related values easily. <br />
<pre>/**
* takes an array of models and their attributes names and outputs them as json. works with relations unlike CJSON::encode()
* @param $models array an array of models, consider using $dataProvider->getData() here
* @param $attributeNames string a comma delimited list of attribute names to output, for relations use relationName.attributeName
* @return void doesn't return anything, but changes content type to json and outputs json and exits
*/
function json_encode_with_relations(array $models, $attributeNames) {
$attributeNames = explode(',', $attributeNames);
$rows = array(); //the rows to output
foreach ($models as $model) {
$row = array(); //you will be copying in model attribute values to this array
foreach ($attributeNames as $name) {
$name = trim($name); //in case of spaces around commas
$row[$name] = CHtml::value($model, $name); //this function walks the relations
}
$rows[] = $row;
}
header('Content-type:application/json');
echo CJSON::encode($rows);
exit(); //or Yii::app()->end() if you want the log to show in debug mode
}
//usage:
$myModels = $myDataProvider->getData(); //or you can use $model->findAll();
$modelAttributeNames = 'id, subject, body, someStatisticalRelationName, someRelationName.attribute, someRelationName.someOtherRelationName.attribute';
json_encode_with_relations( $myModels, $modelAttributeNames );
</pre>Neil McGuiganhttp://www.blogger.com/profile/14122981831780837323noreply@blogger.com4tag:blogger.com,1999:blog-5257555736106762701.post-42974380077079686172011-07-15T09:58:00.000-07:002011-07-15T10:00:57.430-07:00The minimum necessary site structure to run Yii, Part iiIn a <a href="http://learnyii.blogspot.com/2010/12/minimum-necessary-site-structure-to-run.html">previous post</a> , I discussed how to make a minimal site with Yii. Well, here's an even smaller one, with only two files, maybe five lines of code, and no folders:<br />
<br />
index.php:<br />
<pre><?
//load yii framework (you'll likely have to change the path):</pre><pre></pre><pre>require_once( dirname(__FILE__).'/../../yii/framework/yii.php' );
</pre><pre>//create the app. note that it does not require a config file, you can just pass in an array:</pre><pre>$app = Yii::createWebApplication( array (
'basePath' => dirname( __FILE__ ) , // if you don't want Protected folder
'controllerPath' => '' // if you don't want Controllers folder
))->run();
?>
</pre>SiteController.php:<br />
<pre><?
class SiteController extends CController
{
public function actionIndex()
{
echo 'hello world';
}
}
?></pre>Now, this doesn't work with Gii or crazy things like assets, but that's probably the smallest Yii installation you can makeNeil McGuiganhttp://www.blogger.com/profile/14122981831780837323noreply@blogger.com0tag:blogger.com,1999:blog-5257555736106762701.post-76872344712278897382011-07-08T12:24:00.000-07:002011-07-08T12:26:31.596-07:00Using CPropertyValue to convert typesYii's <a href="http://www.yiiframework.com/doc/api/1.1/CPropertyValue">CPropertyValue</a> class is interesting. It has some useful methods to convert values. Some examples:<br />
<pre>var_dump( CPropertyValue::ensureArray( '1,2,a' ) );
/* output: array 0 => string '1,2,3' (length=5) */
var_dump( CPropertyValue::ensureArray( '(1,2,"a")' ) );
/* output:
array
0 => int 1
1=> int 2
2 => string 'a' (length=1)
*/
var_dump( CPropertyValue::ensureBoolean( "TRUE" ) );
/* output: boolean true */
var_dump( CPropertyValue::ensureBoolean( "yii" ) );
/* output: boolean false */
var_dump( CPropertyValue::ensureFloat( "3.9" ) );
/* output: float 3.9 */
var_dump( CPropertyValue::ensureInteger( "3.9" ) );
/* output: int 3 */
var_dump( CPropertyValue::ensureObject( array("a"=>1, "b"=>2) ) );
/* output:
object(stdClass)[53]
public 'a' => int 0
public 'b' => int 1
*/
var_dump( CPropertyValue::ensureString( 5 ) );
/* output: string 5 (length=1) */
</pre>ensureEnum ensures that a value is among a list of enumerated values. pass it in a value and a class name that extends CEnumerable and it will throw an error if value is not among the enumerated values. <br />
<pre>class SortDirection extends CEnumerable
{
const Asc="Asc";
const Desc="Desc";
}
var_dump( CPropertyValue::ensureEnum( "Up" , 'SortDirection' ) );
/* output: throws an error */
var_dump( CPropertyValue::ensureEnum( "Asc" , 'SortDirection' ) );
/* output: string 'Asc' (length=3) */
</pre>Neil McGuiganhttp://www.blogger.com/profile/14122981831780837323noreply@blogger.com0tag:blogger.com,1999:blog-5257555736106762701.post-81648768013572444752011-07-08T11:36:00.000-07:002011-07-08T11:42:28.158-07:00How to use the Filter Validator in YiiHere's a quick example on how to use the <a href="http://www.yiiframework.com/doc/api/1.1/CFilterValidator">Filter Validator</a> in Yii.<br />
<br />
The filter validator isn't actually a validator, it's a filter :) You give it a php function name to run, and it will run that function on the value of the attribute, when validate() is called.<br />
<pre>class Address extends CFormModel /* or CActiveRecord */
{
public $postal_code;
public $street;
public function rules()
{
return array(
//call trim() when validate() is called
array('street', 'filter', 'filter'=>'trim'),
/* call my custom function filterPostalCode when validate() is called
* you can pass it any callback function, but it should only have one parameter
*/
array('postal_code', 'filter', 'filter'=>array( $this, 'filterPostalCode' )),
/* if you are going to filter, then you should put the required validator last, as the validators are called in order */
array('postal_code, street', 'required'),
);
}
public function filterPostalCode($value)
{
//strip out non letters and numbers
$value = preg_replace('/[^A-Za-z0-9]/', '', $value);
return strtoupper($value);
}
}
</pre>Usage:<br />
<pre>$address = new Address;
$address->postal_code ="m5w-1e6";
$address->street = " 123 main street ";
$address->validate();
echo "$address->street $address->postal_code";
$address->postal_code ="/*-*/*-*/-";
$address->street = " ";
$address->validate();
var_dump($address->errors);
</pre>Output:<br />
<br />
<span class="Apple-style-span" style="color: #333333; font-family: 'Trebuchet MS', sans-serif; font-size: 13px; line-height: 18px;">123 main street M5W1E6</span><br />
<pre class="xdebug-var-dump" dir="ltr"><b>array</b>
'postal_code' <span style="color: #888a85;">=></span>
<b>array</b>
0 <span style="color: #888a85;">=></span> <small>string</small> <span style="color: #cc0000;">'Postal Code cannot be blank.'</span> <i>(length=28)</i>
'street' <span style="color: #888a85;">=></span>
<b>array</b>
0 <span style="color: #888a85;">=></span> <small>string</small> <span style="color: #cc0000;">'Street cannot be blank.'</span> <i>(length=23)</i></pre>Neil McGuiganhttp://www.blogger.com/profile/14122981831780837323noreply@blogger.com3tag:blogger.com,1999:blog-5257555736106762701.post-12022636039583076422011-06-29T12:02:00.000-07:002011-06-29T12:02:51.852-07:00CClientScript positioningCClientScript is useful for including css files and javscript files, while avoiding duplication.<br />
<br />
Things to know:<br />
<br />
1. Yii injects css files just above the <title> tag. So, if you want to always override some included yii style, put your <styles> or <link rel=stylesheets> AFTER the <title> tag, and it will get loaded after the yii styles.<br />
<br />
2. I recommend putting all your <styles> and <link rel=stylsheets> in the <head> and all your <scripts> just before </body>. <scripts> are blocking, so your page will load faster if the <scripts> are at the bottom. use:<br />
<br />
Yii::app()->getClientScript()->coreScriptPosition = CClientScript::POS_END;<br />
<br />
3. if you want to include some inline javascript in a view, but make it load at the bottom, after say jquery, use the registerScript() method. To format your js nicely, you can use the <a href="http://www.php.net/manual/en/language.types.string.php#language.types.string.syntax.heredoc">Heredoc </a>syntax:<br />
<br />
/* load some formatted js into a php variable: */<br />
$js = <<<EOF<br />
var = 'some javascript here!';<br />
function() { return 'you can format it as you like, and include php $variables'; };<br />
EOF;<br />
<br />
/* write the script at the bottom of the document */<br />
Yii::app()->getClientScript()->registerScript("some id", $js, CClientScript::POS_END);Neil McGuiganhttp://www.blogger.com/profile/14122981831780837323noreply@blogger.com2tag:blogger.com,1999:blog-5257555736106762701.post-12656414329500982932011-06-16T19:30:00.000-07:002011-06-29T01:44:56.747-07:00Shades of Grey (or Gray :)<table align="center" border="1" cellpadding="1" cellspacing="2" style="border-collapse: collapse;" bgcolor=white><tbody>
<tr><th width="25%">black(Safe 16 SVG Hex3) #000000</th><td bgcolor="#000000" width="8%"><br />
</td><th width="25%">gray1 #030303</th><td bgcolor="#030303" width="8%"></td><th width="25%">gray2 #050505</th><td bgcolor="#050505" width="8%"><br />
</td></tr>
<tr><th>gray3 #080808</th><td bgcolor="#080808"><br />
</td><th>gray4 #0A0A0A</th><td bgcolor="#0A0A0A"></td><th>gray5 #0D0D0D</th><td bgcolor="#0D0D0D"><br />
</td></tr>
<tr><th>gray6 #0F0F0F</th><td bgcolor="#0F0F0F"><br />
</td><th>gray7 #121212</th><td bgcolor="#121212"></td><th>gray8 #141414</th><td bgcolor="#141414"><br />
</td></tr>
<tr><th>gray9 #171717</th><td bgcolor="#171717"><br />
</td><th>gray10 #1A1A1A</th><td bgcolor="#1A1A1A"></td><th>gray11 #1C1C1C</th><td bgcolor="#1C1C1C"><br />
</td></tr>
<tr><th>gray12 #1F1F1F</th><td bgcolor="#1F1F1F"><br />
</td><th>gray13 #212121</th><td bgcolor="#212121"></td><th>gray14 #242424</th><td bgcolor="#242424"><br />
</td></tr>
<tr><th>gray15 #262626</th><td bgcolor="#262626"><br />
</td><th>gray16 #292929</th><td bgcolor="#292929"></td><th>gray17 #2B2B2B</th><td bgcolor="#2B2B2B"><br />
</td></tr>
<tr><th>gray18 #2E2E2E</th><td bgcolor="#2E2E2E"><br />
</td><th>gray19 #303030</th><td bgcolor="#303030"></td><th>gray20(Safe Hex3) #333333</th><td bgcolor="#333333"><br />
</td></tr>
<tr><th>gray21 #363636</th><td bgcolor="#363636"><br />
</td><th>gray22 #383838</th><td bgcolor="#383838"></td><th>gray23 #3B3B3B</th><td bgcolor="#3B3B3B"><br />
</td></tr>
<tr><th>gray24 #3D3D3D</th><td bgcolor="#3D3D3D"><br />
</td><th>gray25 #404040</th><td bgcolor="#404040"></td><th>gray26 #424242</th><td bgcolor="#424242"><br />
</td></tr>
<tr><th>gray27 #454545</th><td bgcolor="#454545"><br />
</td><th>gray28 #474747</th><td bgcolor="#474747"></td><th>gray29 #4A4A4A</th><td bgcolor="#4A4A4A"><br />
</td></tr>
<tr><th>gray30 #4D4D4D</th><td bgcolor="#4D4D4D"><br />
</td><th>gray31 #4F4F4F</th><td bgcolor="#4F4F4F"></td><th>gray32 #525252</th><td bgcolor="#525252"><br />
</td></tr>
<tr><th>gray33(Hex3) #555555</th><td bgcolor="#555555"><br />
</td><th>gray34 #575757</th><td bgcolor="#575757"></td><th>gray35 #595959</th><td bgcolor="#595959"><br />
</td></tr>
<tr><th>gray36 #5C5C5C</th><td bgcolor="#5C5C5C"><br />
</td><th>gray37 #5E5E5E</th><td bgcolor="#5E5E5E"></td><th>gray38 #616161</th><td bgcolor="#616161"><br />
</td></tr>
<tr><th>gray39 #636363</th><td bgcolor="#636363"><br />
</td><th>gray40(Safe Hex3) #666666</th><td bgcolor="#666666"></td><th>dimgrey(SVG) #696969</th><td bgcolor="#696969"><br />
</td></tr>
<tr><th>dimgray(SVG) #696969</th><td bgcolor="#696969"><br />
</td><th>gray42 #6B6B6B</th><td bgcolor="#6B6B6B"></td><th>gray43 #6E6E6E</th><td bgcolor="#6E6E6E"><br />
</td></tr>
<tr><th>gray44 #707070</th><td bgcolor="#707070"><br />
</td><th>gray45 #737373</th><td bgcolor="#737373"></td><th>gray46 #757575</th><td bgcolor="#757575"><br />
</td></tr>
<tr><th>gray47 #787878</th><td bgcolor="#787878"><br />
</td><th>gray48 #7A7A7A</th><td bgcolor="#7A7A7A"></td><th>gray49 #7D7D7D</th><td bgcolor="#7D7D7D"><br />
</td></tr>
<tr><th>grey(16 SVG) #808080</th><td bgcolor="#808080"><br />
</td><th>gray50 #7F7F7F</th><td bgcolor="#7F7F7F"></td><th>gray(16 SVG) #808080</th><td bgcolor="#808080"><br />
</td></tr>
<tr><th>gray51 #828282</th><td bgcolor="#828282"><br />
</td><th>gray52 #858585</th><td bgcolor="#858585"></td><th>gray53 #878787</th><td bgcolor="#878787"><br />
</td></tr>
<tr><th>gray54 #8A8A8A</th><td bgcolor="#8A8A8A"><br />
</td><th>gray55 #8C8C8C</th><td bgcolor="#8C8C8C"></td><th>gray56 #8F8F8F</th><td bgcolor="#8F8F8F"><br />
</td></tr>
<tr><th>gray57 #919191</th><td bgcolor="#919191"><br />
</td><th>gray58 #949494</th><td bgcolor="#949494"></td><th>gray59 #969696</th><td bgcolor="#969696"><br />
</td></tr>
<tr><th>gray60(Safe Hex3) #999999</th><td bgcolor="#999999"><br />
</td><th>gray61 #9C9C9C</th><td bgcolor="#9C9C9C"></td><th>gray62 #9E9E9E</th><td bgcolor="#9E9E9E"><br />
</td></tr>
<tr><th>gray63 #A1A1A1</th><td bgcolor="#A1A1A1"><br />
</td><th>gray64 #A3A3A3</th><td bgcolor="#A3A3A3"></td><th>gray65 #A6A6A6</th><td bgcolor="#A6A6A6"><br />
</td></tr>
<tr><th>darkgray(SVG) #A9A9A9</th><td bgcolor="#A9A9A9"><br />
</td><th>gray66 #A8A8A8</th><td bgcolor="#A8A8A8"></td><th>darkgrey(SVG) #A9A9A9</th><td bgcolor="#A9A9A9"><br />
</td></tr>
<tr><th>gray67 #ABABAB</th><td bgcolor="#ABABAB"><br />
</td><th>sgilightgray(Hex3) #AAAAAA</th><td bgcolor="#AAAAAA"></td><th>gray68 #ADADAD</th><td bgcolor="#ADADAD"><br />
</td></tr>
<tr><th>gray69 #B0B0B0</th><td bgcolor="#B0B0B0"><br />
</td><th>gray70 #B3B3B3</th><td bgcolor="#B3B3B3"></td><th>gray71 #B5B5B5</th><td bgcolor="#B5B5B5"><br />
</td></tr>
<tr><th>gray72 #B8B8B8</th><td bgcolor="#B8B8B8"><br />
</td><th>gray73 #BABABA</th><td bgcolor="#BABABA"></td><th>gray74 #BDBDBD</th><td bgcolor="#BDBDBD"><br />
</td></tr>
<tr><th>silver(16 SVG) #C0C0C0</th><td bgcolor="#C0C0C0"><br />
</td><th>gray #BEBEBE</th><td bgcolor="#BEBEBE"></td><th>gray75 #BFBFBF</th><td bgcolor="#BFBFBF"><br />
</td></tr>
<tr><th>gray76 #C2C2C2</th><td bgcolor="#C2C2C2"><br />
</td><th>gray77 #C4C4C4</th><td bgcolor="#C4C4C4"></td><th>gray78 #C7C7C7</th><td bgcolor="#C7C7C7"><br />
</td></tr>
<tr><th>gray79 #C9C9C9</th><td bgcolor="#C9C9C9"><br />
</td><th>verylightgrey #CDCDCD</th><td bgcolor="#CDCDCD"></td><th>gray80(Safe Hex3) #CCCCCC</th><td bgcolor="#CCCCCC"><br />
</td></tr>
<tr><th>gray81 #CFCFCF</th><td bgcolor="#CFCFCF"><br />
</td><th>gray82 #D1D1D1</th><td bgcolor="#D1D1D1"></td><th>gray83 #D4D4D4</th><td bgcolor="#D4D4D4"><br />
</td></tr>
<tr><th>lightgrey(SVG) #D3D3D3</th><td bgcolor="#D3D3D3"><br />
</td><th>lightgray(SVG) #D3D3D3</th><td bgcolor="#D3D3D3"></td><th>gray84 #D6D6D6</th><td bgcolor="#D6D6D6"><br />
</td></tr>
<tr><th>gray85 #D9D9D9</th><td bgcolor="#D9D9D9"><br />
</td><th>gainsboro(SVG) #DCDCDC</th><td bgcolor="#DCDCDC"></td><th>gray86 #DBDBDB</th><td bgcolor="#DBDBDB"><br />
</td></tr>
<tr><th>gray87 #DEDEDE</th><td bgcolor="#DEDEDE"><br />
</td><th>gray88 #E0E0E0</th><td bgcolor="#E0E0E0"></td><th>gray89 #E3E3E3</th><td bgcolor="#E3E3E3"><br />
</td></tr>
<tr><th>gray90 #E5E5E5</th><td bgcolor="#E5E5E5"><br />
</td><th>gray91 #E8E8E8</th><td bgcolor="#E8E8E8"></td><th>gray92 #EBEBEB</th><td bgcolor="#EBEBEB"><br />
</td></tr>
<tr><th>gray93 #EDEDED</th><td bgcolor="#EDEDED"><br />
</td><th>gray94 #F0F0F0</th><td bgcolor="#F0F0F0"></td><th>gray95 #F2F2F2</th><td bgcolor="#F2F2F2"><br />
</td></tr>
<tr><th>whitesmoke(SVG) #F5F5F5</th><td bgcolor="#F5F5F5"><br />
</td><th>gray97 #F7F7F7</th><td bgcolor="#F7F7F7"></td><th>gray98 #FAFAFA</th><td bgcolor="#FAFAFA"><br />
</td></tr>
<tr><th>gray99 #FCFCFC</th><td bgcolor="#FCFCFC"><br />
</td><th>white(Safe 16 SVG Hex3) #FFFFFF</th><td bgcolor="#FFFFFF"></td><th></th><th></th></tr>
</tbody></table>Neil McGuiganhttp://www.blogger.com/profile/14122981831780837323noreply@blogger.com0tag:blogger.com,1999:blog-5257555736106762701.post-39962301156683391572011-06-16T17:27:00.000-07:002011-06-16T17:28:10.043-07:00Cross-Browser CSS Horizontal Centering<code><br />
/* use for inline (such as text or images) children */<br />
.hcenter-child<br />
{<br />
text-align:center;<br />
}<br />
/*reset the text align, as it inherits*/<br />
.hcenter-child *<br />
{<br />
text-align:left;<br />
}<br />
<br />
/* use for block children (ex divs) */<br />
.hcenter<br />
{<br />
width:96%; /*needs any width other than auto*/<br />
margin-left:auto;<br />
margin-right:auto;<br />
}<br />
</code><br />
<iframe style="width: 100%; height: 300px" src="http://jsfiddle.net/E8DDk/5/embedded/result"></iframe>Neil McGuiganhttp://www.blogger.com/profile/14122981831780837323noreply@blogger.com0tag:blogger.com,1999:blog-5257555736106762701.post-34507335785746895582011-06-16T16:44:00.001-07:002011-06-20T13:06:02.556-07:00Cross-Browser CSS Border Radius<code><br />
.round-borders<br />
{<br />
-moz-border-radius:5px;<br />
-webkit-border-radius:5px;<br />
border-radius:5px;<br />
<br />
/*see http://tumble.sneak.co.nz/post/928998513/fixing-the-background-bleed*/<br />
-moz-background-clip: padding;<br />
-webkit-background-clip: padding-box;<br />
background-clip: padding-box;<br />
</code><code> /*use a behavior file for border radius for IE6-8. see http://www.impressivewebs.com/css3-rounded-corners-in-internet-explorer/ </code><br />
<code>or try the jquery corner plugin */<br />
<br />
}<br />
</code><br />
<br />
<iframe src="http://jsfiddle.net/aBQMe/3/embedded/result" style="height: 300px; width: 100%;"></iframe>Neil McGuiganhttp://www.blogger.com/profile/14122981831780837323noreply@blogger.com0tag:blogger.com,1999:blog-5257555736106762701.post-68739937669258055882011-06-16T16:07:00.000-07:002011-06-18T18:15:34.012-07:00Cross-Browser CSS Opacity<code>.opacity<br />
{<br />
opacity:.5; /*50%*/<br />
filter:alpha(opacity=50);<br />
zoom:1; /*iefix*/<br />
}<br />
</code><br />
<br />
<br />
<iframe src="http://fiddle.jshell.net/SpYpA/8/show/" style="height: 300px; width: 100%;"></iframe>Neil McGuiganhttp://www.blogger.com/profile/14122981831780837323noreply@blogger.com0tag:blogger.com,1999:blog-5257555736106762701.post-28771812752382594382011-06-16T15:47:00.000-07:002011-06-18T18:22:26.792-07:00Cross-Browser CSS Linear Gradients<code><br />
.gradient<br />
{ <br />
<br />
background-color: #DDDDDD; /*fallback*/<br />
background-image: -webkit-gradient(linear, left top, left bottom, from(#FFFFFF), to(#CCCCCC)); /*safari4+,chrome*/<br />
background-image: -webkit-linear-gradient(top, #FFFFFF, #CCCCCC); /*chrome10+,safari5.1+*/<br />
background-image: -moz-linear-gradient(top, #FFFFFF, #CCCCCC); /*ff3.6+*/<br />
background-image: -ms-linear-gradient(top, #FFFFFF, #CCCCCC); /*ie10*/<br />
background-image: -o-linear-gradient(top, #FFFFFF, #CCCCCC); /*opera11.1+*/<br />
background-image: linear-gradient(top, #FFFFFF, #CCCCCC);<br />
<br />
zoom:1; /*iefix*/ <br />
/*GradientType 0=vertical, 1=horizontal*/ <br />
filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0, startColorStr='#FFFFFF', EndColorStr='#CCCCCC'); <br />
<br />
}<br />
</code><br />
<iframe style="width: 100%; height: 300px" src="http://jsfiddle.net/sZX8n/16/embedded/result"></iframe>Neil McGuiganhttp://www.blogger.com/profile/14122981831780837323noreply@blogger.com0tag:blogger.com,1999:blog-5257555736106762701.post-30065276188895455382011-06-16T14:49:00.000-07:002011-06-16T16:10:52.534-07:00Cross-Browser CSS Box ShadowsI'm starting a series on cross-browser CSS.<br />
<br />
Here's how to make a box shadow in Safari, Chrome, Firefox, Opera, and IE5.5+<br />
<code><br />
.box-shadow<br />
{<br />
<br />
/* parameters: horizontal-offset vertical-offset blur-radius color*/<br />
<br />
-moz-box-shadow:5px 5px 5px #cccccc; /*ff3.5+*/<br />
<br />
-webkit-box-shadow:5px 5px 5px #cccccc; /*safari3.2+*/<br />
<br />
box-shadow:5px 5px 5px #cccccc; /*chrome3+, opera10.5+, ie9+*/ <br />
<br />
/* strength=shadow length. direction=angle in degrees clockwise from midnight. color=name or #nnnnnn*/<br />
<br />
filter: progid:DXImageTransform.Microsoft.Shadow(Strength=5, Direction=135, Color=#cccccc); /*ie5.5-8*/<br />
<br />
zoom:1; /*iefix*/<br />
<br />
}<br />
</code><br />
<iframe src="http://jsfiddle.net/jN8Yv/25/embedded/result" style="height: 300px; width: 100%;"></iframe>Neil McGuiganhttp://www.blogger.com/profile/14122981831780837323noreply@blogger.com0tag:blogger.com,1999:blog-5257555736106762701.post-17381798840419695742011-06-14T16:45:00.000-07:002011-06-14T20:46:19.359-07:00How to fix font and color issues when you put a yii widget inside a jquery widgetIf you put a Yii widget inside a jQueryUI widget, then it doesn't look quite right. Here's how to fix it<br />
<br />
put this style sheet after you load jQueryUI's style sheet:<br />
<code><br />
<style><br />
/*use your site's font and font size*/<br />
.ui-widget {font-family:inherit; font-size:inherit;}<br />
<br />
/* make form controls inside jqueryui widgets use site font */<br />
.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button {font-family:inherit; }<br />
<br />
/* links go black inside jqueryui. this changes colour of autocomplete however... */<br />
.ui-widget-content a {color:#06C;}<br />
</style><br />
</code><br />
Alternatively, you could just edit jQueryUI's style sheet, or roll your own theme.<br />
<br />
Also, the blueprint css framework makes jQueryUI's datepicker look wrong. Look for the typography section of screen.css, and comment out this section:<br />
<code><br />
/* thead th { background: #c3d9ff; } */<br />
</code>Neil McGuiganhttp://www.blogger.com/profile/14122981831780837323noreply@blogger.com0tag:blogger.com,1999:blog-5257555736106762701.post-30249356119027156222011-04-28T16:27:00.000-07:002011-06-18T23:49:01.516-07:00Yii » Missing FeaturesThis will be an ongoing list. Check back in the future.<br />
<br />
<ol><li>Yii needs a grid that you can edit inline - maybe <a href="http://www.trirand.net/demophp.aspx">jqgrid</a> for version 2?</li>
<li>The listData->groupField works nicely on dropDownLists, but it would be nice it it worked on checkBoxLists and radioButtonLists too (displaying the category name and indenting associated controls)</li>
<li>No concurrency control. It would be nice in Gii if it could generate models that saved the original record values, as well as using them to check for concurrency when saving. </li>
<li>Easier to use some of the new HTML 5 controls, like "search"</li>
<li>Built-in basic search on lists (much like on grids)</li>
<li>Easier tabular input<span id="goog_683543101"></span><span id="goog_683543102"></span><a href="http://www.blogger.com/"></a></li>
<li>When you do ajax on a grid (sort or page), it reloads the whole page, finds the grid content, and replaces itself. This is a little inefficient ;) . It would be better if Gii could split the grids out to separate partial views. </li>
<li>Yii seems to get confused sometimes when you have nested JQuery UI controls</li>
<li>It would be better if the built-in Blueprint theme was mobile-friendly out of the box, and by that I mean side by side columns that collapse on top of each other on small width screens, perhaps switch to the <a href="http://cssgrid.net/">1140 grid system</a>. </li>
<li>use superfish on multi-level menus out' the box</li>
<li>ZeroClipboard, jQuery WYSIWYG editor, and fullcalendar support out' the box.</li>
<li>phpMailer, a flash/html5/silverlight multi-uploader, vcard, pdf, ics support out' the box</li>
</ol>Neil McGuiganhttp://www.blogger.com/profile/14122981831780837323noreply@blogger.com0tag:blogger.com,1999:blog-5257555736106762701.post-90899935478600465352011-04-17T11:24:00.000-07:002011-04-17T11:27:15.999-07:00How to hide the url address bar on a web page on BlackberryHere's how to minimize the url address bar in a web page on a blackberry (blackberry 6 anyways).<br />
<br />
Put this script at the bottom of the page, or call it ondocumentready:<br />
<br />
<tt>if(navigator.userAgent.toLowerCase().indexOf('blackberry')>0)window.scrollTo(0,40);</tt><br />
<br />
It causes the browser to scroll down 40 pixels, which essentially makes the browser bar hide itself.Neil McGuiganhttp://www.blogger.com/profile/14122981831780837323noreply@blogger.com3