root/trunk/CHANGELOG.txt

Revision 977, 15.0 kB (checked in by bermiferrer, 1 month ago)

Adding the Action Mailer framework, for sending emails from your Akelos application.

ActionMailer? is an Akelos service-layer package for creating email messages.

=== Generate a mailer ===

Creates a stub file using the generator:

./script/generate mailer Notifier

A file is created in your app/models directory named notifier.php:

class Notifier extends AkActionMailer{
}

There are some new conventions for the Action Mailer:

  • Action Mailer implementations have views. The above example will reference files in app/views/notifier
  • Each email that you want to send should have one associated method within the class.
  • You don't call that method. Ever.
  • Instead, you call some dynamic methods, as we'll see below.

=== Example: ===

==== The scenario ====

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.

=== Notifier ===

Add the following method to your Notifier:

function signup_thanks($User){ 
  //Email header info MUST be added here
  $this->set(array( 
    'recipients' => $User->email, 
    'from' => 'accounts@example.com', 
    'subject' => 'Thank you for registering with our website', 
    // Email body parameters (for the view) go here 
    'body' => array( 'User' => $User ) 
    )); 
}

=== Controller ===

And the following snippet to the controller in your application that needs to send an email:

function show_page_after_account_creation(){ 
  Ak::import_mailer('notifier'); 
  $Notifier = new Notifier(); 
  $Notifier->deliver('signup_thanks', $this->User); 
} 

You could also import the model into you controller as usual, but before you'll need to require(AK_LIB_'DIR.'/AkActionMailer.php').

=== View ===

The body of the email comes from a .tpl file in app/views/notifier - In this example; app/views/notifier/signup_thanks.tpl:

Dear {User.first_name} {User.last_name},
Thanks for signing up with us!

==== HTML and Text messages ====

If you want to send two alternative messages (text/plain and text/html), the Akelos convention is to have two views like:

app/views/notifier/signup_thanks.text.plain.tpl
app/views/notifier/signup_thanks.text.html.tpl

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.

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.


Added new system for retrieving configurations from YAML files.

You just need to call Ak::getSettings($namespace), where namespace is "config/$namespace.yml"


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::avoidcopyingviews to true.


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.


Whenever a charset is not supported by mb_* AkelosAkCharset? will now default to the PhpRecoding? engine on that specific recoding task.


Adding Ak::first() and Ak::last(), which work like PHP functions shift(), and pop() but without modifying source array.


Added a new system for unifying in-function static vars used mainly for performance improvements framework-wide.

Before we had

class A{
  function b($var){
    static $chache;
    if(!isset($cache[$var])){
      $cache[$var] = some_heavy_function($var);
    }
    return $cache[$var]; 
  } 
}

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.

We can refresh this static values the new Ak::getStaticVar method. So from previous example we will have to replace

static $chache;

with

$chache =& Ak::getStaticVar(__CLASS__.'::'.__FUNCTION__);

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.

Line 
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
Note: See TracBrowser for help on using the browser.