| 1 |
SVN |
|---|
| 2 |
|
|---|
| 3 |
* [977] Adding the Action Mailer framework, for sending emails from your Akelos application. |
|---|
| 4 |
|
|---|
| 5 |
ActionMailer is an Akelos service-layer package for creating email messages. |
|---|
| 6 |
|
|---|
| 7 |
=== Generate a mailer === |
|---|
| 8 |
|
|---|
| 9 |
Creates a stub file using the generator: |
|---|
| 10 |
|
|---|
| 11 |
{{{ |
|---|
| 12 |
./script/generate mailer Notifier |
|---|
| 13 |
}}} |
|---|
| 14 |
|
|---|
| 15 |
A file is created in your app/models directory named notifier.php: |
|---|
| 16 |
|
|---|
| 17 |
{{{ |
|---|
| 18 |
class Notifier extends AkActionMailer{ |
|---|
| 19 |
} |
|---|
| 20 |
}}} |
|---|
| 21 |
|
|---|
| 22 |
There are some new conventions for the Action Mailer: |
|---|
| 23 |
|
|---|
| 24 |
* Action Mailer implementations have views. The above example will reference files in app/views/notifier |
|---|
| 25 |
* Each email that you want to send should have one associated method within the class. |
|---|
| 26 |
* You don’t call that method. Ever. |
|---|
| 27 |
* Instead, you call some dynamic methods, as we’ll see below. |
|---|
| 28 |
|
|---|
| 29 |
=== Example: === |
|---|
| 30 |
|
|---|
| 31 |
==== The scenario ==== |
|---|
| 32 |
|
|---|
| 33 |
Let’s assume we’re sending an email to a new user to our website to thank them for signing up, and that user is an object coming from an Active Record that has already been created. |
|---|
| 34 |
|
|---|
| 35 |
=== Notifier === |
|---|
| 36 |
|
|---|
| 37 |
Add the following method to your Notifier: |
|---|
| 38 |
|
|---|
| 39 |
{{{ |
|---|
| 40 |
function signup_thanks($User){ |
|---|
| 41 |
//Email header info MUST be added here |
|---|
| 42 |
$this->set(array( |
|---|
| 43 |
'recipients' => $User->email, |
|---|
| 44 |
'from' => 'accounts@example.com', |
|---|
| 45 |
'subject' => 'Thank you for registering with our website', |
|---|
| 46 |
// Email body parameters (for the view) go here |
|---|
| 47 |
'body' => array( 'User' => $User ) |
|---|
| 48 |
)); |
|---|
| 49 |
} |
|---|
| 50 |
}}} |
|---|
| 51 |
|
|---|
| 52 |
=== Controller === |
|---|
| 53 |
|
|---|
| 54 |
And the following snippet to the controller in your application that needs to send an email: |
|---|
| 55 |
|
|---|
| 56 |
{{{ |
|---|
| 57 |
function show_page_after_account_creation(){ |
|---|
| 58 |
Ak::import_mailer('notifier'); |
|---|
| 59 |
$Notifier = new Notifier(); |
|---|
| 60 |
$Notifier->deliver('signup_thanks', $this->User); |
|---|
| 61 |
} |
|---|
| 62 |
}}} |
|---|
| 63 |
|
|---|
| 64 |
You could also import the model into you controller as usual, but before you'll need to `require(AK_LIB_'DIR.'/AkActionMailer.php')`. |
|---|
| 65 |
|
|---|
| 66 |
=== View === |
|---|
| 67 |
|
|---|
| 68 |
The body of the email comes from a .tpl file in app/views/notifier – In this example; app/views/notifier/signup_thanks.tpl: |
|---|
| 69 |
|
|---|
| 70 |
{{{ |
|---|
| 71 |
Dear {User.first_name} {User.last_name}, |
|---|
| 72 |
Thanks for signing up with us! |
|---|
| 73 |
}}} |
|---|
| 74 |
|
|---|
| 75 |
==== HTML and Text messages ==== |
|---|
| 76 |
|
|---|
| 77 |
If you want to send two alternative messages (text/plain and text/html), the Akelos convention is to have two views like: |
|---|
| 78 |
|
|---|
| 79 |
{{{ |
|---|
| 80 |
app/views/notifier/signup_thanks.text.plain.tpl |
|---|
| 81 |
app/views/notifier/signup_thanks.text.html.tpl |
|---|
| 82 |
}}} |
|---|
| 83 |
|
|---|
| 84 |
If you add images to your html message Akelos will embed them automatically into the message body unless you set the attribute `_attach_html_images` to false in you mailer. |
|---|
| 85 |
|
|---|
| 86 |
--- |
|---|
| 87 |
|
|---|
| 88 |
You can find more documentation on the Action Mailer at the AkActionMailer inline documentation and on the unit tests, document configuration, helpers and receiving emails. |
|---|
| 89 |
|
|---|
| 90 |
|
|---|
| 91 |
|
|---|
| 92 |
* [977] Added new system for retrieving configurations from YAML files. |
|---|
| 93 |
|
|---|
| 94 |
You just need to call Ak::getSettings($namespace), where namespace is "config/$namespace.yml" |
|---|
| 95 |
|
|---|
| 96 |
* [977] Added new AkMailerTest, for testing ActionMailer models. This tester will copy your application views from the app/views to test/fixtures/app/views unless you implicitly set AkMailerTest::avoid''copying''views to true. |
|---|
| 97 |
* [977] Adding Ak::import_mailer() which works like Ak::import(), but imports lib/AkActionMailer.php before doing so. We might deprecate this once we drop support for PHP4. |
|---|
| 98 |
* [977] Whenever a charset is not supported by mb_* AkelosAkCharset will now default to the PhpRecoding engine on that specific recoding task. |
|---|
| 99 |
* [977] Adding Ak::first() and Ak::last(), which work like PHP functions shift(), and pop() but without modifying source array. |
|---|
| 100 |
* [977] Added a new system for unifying in-function static vars used mainly for performance improvements framework-wide. |
|---|
| 101 |
|
|---|
| 102 |
Before we had |
|---|
| 103 |
|
|---|
| 104 |
{{{ |
|---|
| 105 |
class A{ |
|---|
| 106 |
function b($var){ |
|---|
| 107 |
static $chache; |
|---|
| 108 |
if(!isset($cache[$var])){ |
|---|
| 109 |
$cache[$var] = some_heavy_function($var); |
|---|
| 110 |
} |
|---|
| 111 |
return $cache[$var]; |
|---|
| 112 |
} |
|---|
| 113 |
} |
|---|
| 114 |
}}} |
|---|
| 115 |
|
|---|
| 116 |
Now imagine we want to create an application server which handles multiple requests on a single instantiation, with the showcased implementation this is not possible as we can't reset $cache, unless we hack badly every single method that uses this strategy. |
|---|
| 117 |
|
|---|
| 118 |
We can refresh this static values the new `Ak::getStaticVar` method. So from previous example we will have to replace |
|---|
| 119 |
|
|---|
| 120 |
{{{ |
|---|
| 121 |
static $chache; |
|---|
| 122 |
}}} |
|---|
| 123 |
|
|---|
| 124 |
with |
|---|
| 125 |
|
|---|
| 126 |
{{{ |
|---|
| 127 |
$chache =& Ak::getStaticVar(__CLASS__.'::'.__FUNCTION__); |
|---|
| 128 |
}}} |
|---|
| 129 |
|
|---|
| 130 |
You should not use this strategy on those situations where static vars should never be modified from other methods different of the model implementing it. |
|---|
| 131 |
|
|---|
| 132 |
|
|---|
| 133 |
* [556] Adding a new HTTP client for performing REST requests at lib/AkHttpClient. See unit tests for details. |
|---|
| 134 |
|
|---|
| 135 |
* [556] Adding support for accepting PUT HTTP requests. |
|---|
| 136 |
|
|---|
| 137 |
* [556] Changing Ak::DeprecateWarning to Ak::deprecateWarning in order to match coding conventions. |
|---|
| 138 |
|
|---|
| 139 |
* [556] Adding support for reporting unknown actions using a public/405.php file when working on production mode. 404.php remains the file for controller not found errors. |
|---|
| 140 |
|
|---|
| 141 |
* [556] Logging invalid controller/action requests. |
|---|
| 142 |
|
|---|
| 143 |
* [556] Modifying Ak::url_get_contents. It does not require curl anymore. |
|---|
| 144 |
|
|---|
| 145 |
Now it works like |
|---|
| 146 |
|
|---|
| 147 |
{{{ |
|---|
| 148 |
$Client = new AkHttpClient(); |
|---|
| 149 |
$Client->get('url', array('params'=>.....)); |
|---|
| 150 |
}}} |
|---|
| 151 |
|
|---|
| 152 |
* [549] AK_COMPILED_VIEWS_DIR is now set to AK_TMP_DIR/views. This means your compiled views will be generated inside the tmp folder unless you set AK_COMPILED_VIEWS_DIR to false to keep them side to side to your views. |
|---|
| 153 |
|
|---|
| 154 |
* [549] On those cases where your host do not support FTP and Apache runs as nobody, you will need to set the right permissions for ./app/locales if you want new i18n text to be added automagically. Same for ./logs. Compiled views and cache will go into a temporary folder. |
|---|
| 155 |
|
|---|
| 156 |
* [549] In those cases where ./tmp is not writable, Akelos will set AK_TMP_DIR to a new folder in your Operating System tmp dir. |
|---|
| 157 |
|
|---|
| 158 |
This sometimes might lead to problems when Open Base forbids including files from the temporary path. In that case you'll need to chmod the ./tmp dir so Akelos knows where to put tmp files. |
|---|
| 159 |
|
|---|
| 160 |
In case you want to make a portable app that needs to write/read files, you can set the AK_TMP_DIR as a base for working with those files like: |
|---|
| 161 |
|
|---|
| 162 |
{{{ |
|---|
| 163 |
Ak::file_puts_contents($dir, $content, array('base_path'=>AK_TMP_DIR)); |
|---|
| 164 |
}}} |
|---|
| 165 |
|
|---|
| 166 |
* [511] Action Controllers now will automatically load singular named helpers and models if the controller has the plural form. |
|---|
| 167 |
|
|---|
| 168 |
* [510] Added a handy utility for modifying arrays which is useful for securing record creation/updating. |
|---|
| 169 |
|
|---|
| 170 |
If you have this code on a controller |
|---|
| 171 |
{{{ |
|---|
| 172 |
$this->user->setAttributes($this->params['user']); |
|---|
| 173 |
}}} |
|---|
| 174 |
|
|---|
| 175 |
and your users table has a column named is_admin. All it would take to a malicious user is to modify the page html to add the need field and gain admin privileges. |
|---|
| 176 |
|
|---|
| 177 |
You could avoid it by using the new Ak::pick method which will return and array with desired keys. For deleting keys instead of selecting them you can use Ak::delete() |
|---|
| 178 |
|
|---|
| 179 |
{{{ |
|---|
| 180 |
$this->user->setAttributes(Ak::pick('name,email', $this->params['user'])); |
|---|
| 181 |
}}} |
|---|
| 182 |
|
|---|
| 183 |
* [509] Parameters given to url_for do not need to be urlencoded anymore. This allows using variables with slashes. |
|---|
| 184 |
|
|---|
| 185 |
* [476] Adding includeAndInstatiateModels and uninstallAndInstallMigration to AkUnitTest |
|---|
| 186 |
|
|---|
| 187 |
* Making it possible to include models by default by declaring in your application_controller.php |
|---|
| 188 |
|
|---|
| 189 |
{{{ |
|---|
| 190 |
var $app_models = 'user,role'; |
|---|
| 191 |
}}} |
|---|
| 192 |
|
|---|
| 193 |
this way you can avoid adding controller-wide models into every single controller. |
|---|
| 194 |
|
|---|
| 195 |
* HTTP authentication now can have its own failure message by implementing a **access_denied** method into the application controller. |
|---|
| 196 |
|
|---|
| 197 |
---------------------- |
|---|
| 198 |
|
|---|
| 199 |
* Model generator now supports customizing first migration version column setting from the command line like: |
|---|
| 200 |
|
|---|
| 201 |
./generate model Video title, length double, author, is_searchable |
|---|
| 202 |
|
|---|
| 203 |
* Relocated Active Record database adapters, behaviors and associations into lib/AkActiveRecord/AkDbAdapters, AkActsAsBehaviours and AkAssociations respectively. |
|---|
| 204 |
|
|---|
| 205 |
---------- |
|---|
| 206 |
|
|---|
| 207 |
## Merging Kaste's branch with the trunk. |
|---|
| 208 |
|
|---|
| 209 |
WARNING: IMPORTANT CHANGES AHEAD! |
|---|
| 210 |
|
|---|
| 211 |
* [387] Refactored AkActiveRecord::find(). |
|---|
| 212 |
* [387] API-change: removed find('first', $id, $options) cause finding one id is always "first". Use find($id, $options); instead. |
|---|
| 213 |
* [395] Deprecated AkActiveRecord::find($sql) which silently expanded to AkActiveRecord::find('first', $sql). |
|---|
| 214 |
This was ambiguous because find($sql, $bind_variables*) expanded to find('all',*) |
|---|
| 215 |
Use AkActiveRecord::find('first', $sql) instead. |
|---|
| 216 |
* [396] Added AkDbAdapter between ADODb and the Active Record. |
|---|
| 217 |
* [396] Expanded AkUnitTest so its easy to generate Models on the fly like: |
|---|
| 218 |
|
|---|
| 219 |
$AkUnitTest->installAndIncludeModels(array('Article'=>'id,name,description')); |
|---|
| 220 |
|
|---|
| 221 |
Creates the table 'articles' with specified columns and builds the ActiveRecord Model (class) 'Article'. |
|---|
| 222 |
* [396] AkActiveRecord::toYaml now handles ActiveRecord collections. |
|---|
| 223 |
|
|---|
| 224 |
User::toYaml($found_users); |
|---|
| 225 |
|
|---|
| 226 |
* [407] Fixed typos on the Active Record like $asssociated_ids.... |
|---|
| 227 |
* [407] Improved and refactored Active Record unit tests. |
|---|
| 228 |
* [405] Fixed AkHasAndBelongsToMany/AkHasMany::getAssociatedModelInstance which Singleton was badly implemented. |
|---|
| 229 |
* [416] Avoided unsetting database profiles. |
|---|
| 230 |
* [416] Added support for extra database profiles you can quickly test with different db-adapters like: |
|---|
| 231 |
|
|---|
| 232 |
ActiveRecord::establishConnection('super_user'); |
|---|
| 233 |
./test _some_test_case.php sqlite_test_profile |
|---|
| 234 |
|
|---|
| 235 |
* [417] Changed NewDataDictionary(db->connection) to db->getDictionary(); |
|---|
| 236 |
* [418] AkHasAndBelongsToMany now uses AkInstaller to create the join table. Because of that it creates the sequence_table for sqlite straight away. |
|---|
| 237 |
* [419] Fixed singleton implementation of Ak::getLogger() |
|---|
| 238 |
* [425] AkInstaller: Added magic 'lock_version' column. |
|---|
| 239 |
|
|---|
| 240 |
'lock_version' => 'lock_version int default=1' |
|---|
| 241 |
|
|---|
| 242 |
* [427] Refactored the Active Record "callback"-chain. Fixes #95 and #94. |
|---|
| 243 |
* [427] Added create, update and execute methods to AkDbAdapter. |
|---|
| 244 |
* [428] Adding support for late bindings on AkDbAdapter::execute() |
|---|
| 245 |
This allows you to safely sanitize parameters before adding them to your custom SQL queries. |
|---|
| 246 |
|
|---|
| 247 |
AkDbAdapter::execute(array('select * from articles where id=?', 1)); |
|---|
| 248 |
|
|---|
| 249 |
* [429] Added addLimitAndOffset method to AkDbAdapter for delegating limits and offsets. |
|---|
| 250 |
* [431] Implemented renameColumn in AkDbAdapter. Closes #47 and #96. |
|---|
| 251 |
* [436] Removed AkActiveRecord::sqlSelect* (now in AkDbAdapter) |
|---|
| 252 |
* [437] Moved transactions to AkDbAdapter |
|---|
| 253 |
* [439] Fixed a serious issue in a TEST that could lead to data loss in the development database. |
|---|
| 254 |
* [441] Replacing MetaTables() and MetaColumns() with AkDbAdapter::availableTables() and AkDbAdapter::getColumnDetails($table). |
|---|
| 255 |
* [446] Improved the MenuHeper |
|---|
| 256 |
* [446] Changed default options for AkPluginManager::updatePlugin(). Disables externals and checkout. |
|---|
| 257 |
* [448] AkActiveRecord::findBySql now uses AkDbAdapter::select |
|---|
| 258 |
* [450] AkActiveRecord::incrementCounter() and decrementCounter() now are pseudo-static. |
|---|
| 259 |
* [450] AkActiveRecord::updateAttribute() now validates when saving by default. Pass 'false' as third argument to bypass validation. |
|---|
| 260 |
* [451] AkInstaller automatically sets '*_count'-columns => 'columnname int default 0' |
|---|
| 261 |
* [458] Fixed #103, quoting strings on PostgreSQL. |
|---|
| 262 |
* [459] Adding decimal-type support on Active Records. |
|---|
| 263 |
* [459] Datatypes for PostgreSQL changed. You need to update/change your table schemas! Run migrations! |
|---|
| 264 |
Before this, we kinda hacked Mysql-behavior into PostgreSQL. Thus we didnt used features of Postgre on one side. |
|---|
| 265 |
|
|---|
| 266 |
In the long run we had to fix - better now than later - since the design problems only "multiply" when time goes by. |
|---|
| 267 |
|
|---|
| 268 |
At this point we wanted to implement the decimal/numeric datatype. And so we had to decide whether to hack further or to solve the underlying issue. This means we HAD to correct a wrong implementation. |
|---|
| 269 |
|
|---|
| 270 |
(simplified type>) Akelos Postgre (<Actual Type) |
|---|
| 271 |
|
|---|
| 272 |
Until now we had: |
|---|
| 273 |
|
|---|
| 274 |
boolean => numeric(1) |
|---|
| 275 |
integer => numeric(X,0) |
|---|
| 276 |
|
|---|
| 277 |
From now on we have: |
|---|
| 278 |
boolean => bool |
|---|
| 279 |
integer => integer (int4) |
|---|
| 280 |
decimal => numeric |
|---|
| 281 |
|
|---|
| 282 |
To guide you through this we'll have a test at test/unit/lib/AkActiveRecord/_PostgreSQL_datatype_migration.php. |
|---|
| 283 |
|
|---|
| 284 |
First make you comfortably with this test and make it pass. This is a test against a dummy-table of course. |
|---|
| 285 |
(When you're on Postgre 7 you have to modify this test. But you'll see that.) |
|---|
| 286 |
|
|---|
| 287 |
Next write appropriate migrations/installers for your real tables. (Again: You should always begin with a test.) |
|---|
| 288 |
|
|---|
| 289 |
Keep in mind that we typecast TINYINT as boolean on MySQL. So you cannot use tinyint for other things. |
|---|
| 290 |
* [459] Boolean columns now actually have three possible states: true, false and null. Before that null=>false! |
|---|
| 291 |
* [461] ActiveRecordHelper::error_messages_for and error_message_on now translate the error messages. |
|---|
| 292 |
* [467] NULL values can be saved on boolean and decimal columns. Fixes #114 and #113. |
|---|
| 293 |
|
|---|
| 294 |
#### End of Kaste merge |
|---|
| 295 |
|
|---|
| 296 |
----------- |
|---|
| 297 |
|
|---|
| 298 |
* AkInstaller::createTable() will now add created_at and updated_at columns automatically unless you have one of |
|---|
| 299 |
them in your table declaration or set the option 'timestamp' => false |
|---|
| 300 |
|
|---|
| 301 |
function up_1(){ |
|---|
| 302 |
$this->createTable('user', 'id, first_name, last_name, email'); // will add created_at, and updated_at |
|---|
| 303 |
} |
|---|
| 304 |
|
|---|
| 305 |
to avoid it |
|---|
| 306 |
|
|---|
| 307 |
function up_1(){ |
|---|
| 308 |
$this->createTable('user', 'id, first_name, last_name, email', array('timestamp'=>false)); // nothing extra |
|---|
| 309 |
} |
|---|
| 310 |
|
|---|
| 311 |
or |
|---|
| 312 |
|
|---|
| 313 |
function up_1(){ |
|---|
| 314 |
$this->createTable('user', 'id, first_name, last_name, email, updated_at'); // nothing extra |
|---|
| 315 |
} |
|---|
| 316 |
|
|---|
| 317 |
* Simplifying unit test calls for models and core tests. Updated generators to reflect this new way of calling tests. |
|---|
| 318 |
If you stick with the convention of prefixing your test cases with TestCase you will no longer need to call ak__test('testcaseclass') |
|---|
| 319 |
|
|---|
| 320 |
Running models test can now be done with simply |
|---|
| 321 |
./script/test model User |
|---|
| 322 |
|
|---|
| 323 |
Core tests can be called without the full path like |
|---|
| 324 |
./script/test AkActiveRecord |
|---|
| 325 |
|
|---|
| 326 |
* Rearranged scripts to include as little code as possible in the application space. This should make updates easier. |
|---|
| 327 |
|
|---|
| 328 |
* Removed AkInflector::modulize as it had a misleading name, use AkInflector::classify instead [420] |
|---|
| 329 |
|
|---|
| 330 |
* Added support for HTTP Authentication [412]. Example: |
|---|
| 331 |
|
|---|
| 332 |
<?php |
|---|
| 333 |
|
|---|
| 334 |
class PostController extends ApplicationController |
|---|
| 335 |
{ |
|---|
| 336 |
var $_authorized_users = array('bermi' => 'secret'); |
|---|
| 337 |
|
|---|
| 338 |
function __construct() |
|---|
| 339 |
{ |
|---|
| 340 |
$this->beforeFilter(array('authenticate' => array('except' => array('index')))); |
|---|
| 341 |
} |
|---|
| 342 |
|
|---|
| 343 |
function index() |
|---|
| 344 |
{ |
|---|
| 345 |
$this->renderText("Everyone can see me!"); |
|---|
| 346 |
} |
|---|
| 347 |
|
|---|
| 348 |
function edit() |
|---|
| 349 |
{ |
|---|
| 350 |
$this->renderText("I'm only accessible if you know the password"); |
|---|
| 351 |
} |
|---|
| 352 |
|
|---|
| 353 |
function authenticate() |
|---|
| 354 |
{ |
|---|
| 355 |
/** |
|---|
| 356 |
* You can either use an array like $this->_authorized_users or |
|---|
| 357 |
* an Model instance that implements an authenticate method like Model::authenticate($user,$pass, $controller); |
|---|
| 358 |
*/ |
|---|
| 359 |
return $this->_authenticateOrRequestWithHttpBasic('My Blog', $this->_authorized_users); |
|---|
| 360 |
} |
|---|
| 361 |
} |
|---|
| 362 |
|
|---|
| 363 |
?> |
|---|
| 364 |
|
|---|
| 365 |
* Added public/500.php and public/404.php for handling errors on production mode. |
|---|
| 366 |
|
|---|
| 367 |
|
|---|
| 368 |
0.8 |
|---|
| 369 |
---------------------- |
|---|
| 370 |
|
|---|
| 371 |
* First public release |
|---|
| 372 |
|
|---|
| 373 |
|
|---|