分類彙整: program

Yii ActiveRecord空字串存成Null

因為搞了快1個小時
搞定後~~筆記下來…

直接下Models 裡面的 rules()
設定

public function rules()
{
  array('column1,column2', 'default', 'setOnEmpty' => true, 'value' => null),
}

ref. http://www.yiiframework.com/forum/index.php?/topic/5448-blank-fields-in-forms-insert-replace-default-null-columns-with-empty-string/

Cordova / Phonegap PushNotification tutorial ( iOS限定 )

前言
執行下列monkey step 前, 請先閱讀 Phonegap/Cordova 的 installation guide 建立一個 Project 出來
我用到的 Phonegap Plugin 是 PushNotification, 及加上  EasyAPNS

1. 先產生 CSR.
2. 在 iOS Provisioning Portal 中 new 一個 APP IDs, 然後 configure,  Enable for Development, Enable for Production
3. 下載 aps_development.cer, aps_production.cer, double click 這兩個file 匯進key acess
4. 建立一個 新的 Provisioning profile 選擇剛剛新建的  App IDs.
5. 下載此Provisioning profile.
6. 打開 Xcode, 選擇organizer 把 Provisioning profile 的檔案 拉進去
7. 選擇 Project, 將Build Settings 裡面的Code Signing identity 選擇成 你所建立的 App ID
8. 在Classes 按右鍵選 Add Files to “xxx”, 將這兩個檔案加上去 PushNotification.m, PushNotification.h 這兩個檔案 ( PushNotification 是 Phonegap 的Plugin )
9. 放 PushNotification.js, jquery 及 jquery mobile 的相關檔案 放到 www folder
10. 開啟 Cordova.plist

  • 在 ExternalHosts 加上 一筆 * 的 record , key 用default 的即可。
  • 在 Plugins 加上一筆 key -> PushNotification, value -> PushNotification

11. 打開 AppDelegate.h 加上

#import <AudioToolbox/AudioToolbox.h>
#import "PushNotification.h"

12. 打開 AppDelegate.m

  1. 在 didFinishLaunchingWithOptions 這個 method 裡上方加上
    // Add registration for remote notifications
    [[UIApplication sharedApplication]
    	registerForRemoteNotificationTypes:(UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)];
    
    // Clear application badge when app launches
    application.applicationIconBadgeNumber = 0;
  2. 在 didFinishLaunchingWithOptions 這個 method 裡頭的 [self.window makeKeyAndVisible]; 和 return YES; 中間插
    [self.window makeKeyAndVisible];
    
    /* START BLOCK */
    
    // PushNotification - Handle launch from a push notification
    NSDictionary* userInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
    if(userInfo) {
        PushNotification *pushHandler = [self.viewController getCommandInstance:@"PushNotification"];
        NSMutableDictionary* mutableUserInfo = [userInfo mutableCopy];
        [mutableUserInfo setValue:@"1" forKey:@"applicationLaunchNotification"];
        [mutableUserInfo setValue:@"0" forKey:@"applicationStateActive"];
        [pushHandler.pendingNotifications addObject:mutableUserInfo];
    }
    
    /* STOP BLOCK */
    
    return YES;
  3. dealloc method and the implementation @end. 中間插入
    - (void) dealloc
    {
    	[super dealloc];
    }
    
    /* START BLOCK */
    
    /**
     * Fetch and Format Device Token and Register Important Information to Remote Server
     */
    - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)devToken {
    
    #if !TARGET_IPHONE_SIMULATOR
        /* EasyAPNS Start Block */
    
    	// Get Bundle Info for Remote Registration (handy if you have more than one app)
    	NSString *appName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"];
    	NSString *appVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"];
    
    	// Check what Notifications the user has turned on.  We registered for all three, but they may have manually disabled some or all of them.
    	NSUInteger rntypes = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
    
    	// Set the defaults to disabled unless we find otherwise...
    	NSString *pushBadge = @"disabled";
    	NSString *pushAlert = @"disabled";
    	NSString *pushSound = @"disabled";
    
    	// Check what Registered Types are turned on. This is a bit tricky since if two are enabled, and one is off, it will return a number 2... not telling you which
    	// one is actually disabled. So we are literally checking to see if rnTypes matches what is turned on, instead of by number. The "tricky" part is that the
    	// single notification types will only match if they are the ONLY one enabled.  Likewise, when we are checking for a pair of notifications, it will only be
    	// true if those two notifications are on.  This is why the code is written this way
    	if(rntypes == UIRemoteNotificationTypeBadge){
    		pushBadge = @"enabled";
    	}
    	else if(rntypes == UIRemoteNotificationTypeAlert){
    		pushAlert = @"enabled";
    	}
    	else if(rntypes == UIRemoteNotificationTypeSound){
    		pushSound = @"enabled";
    	}
    	else if(rntypes == ( UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert)){
    		pushBadge = @"enabled";
    		pushAlert = @"enabled";
    	}
    	else if(rntypes == ( UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)){
    		pushBadge = @"enabled";
    		pushSound = @"enabled";
    	}
    	else if(rntypes == ( UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound)){
    		pushAlert = @"enabled";
    		pushSound = @"enabled";
    	}
    	else if(rntypes == ( UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound)){
    		pushBadge = @"enabled";
    		pushAlert = @"enabled";
    		pushSound = @"enabled";
    	}
    
    	// Get the users Device Model, Display Name, Unique ID, Token & Version Number
    	UIDevice *dev = [UIDevice currentDevice];
    	NSString *deviceUuid = dev.uniqueIdentifier;
        NSString *deviceName = dev.name;
    	NSString *deviceModel = dev.model;
    	NSString *deviceSystemVersion = dev.systemVersion;
    
    	// Prepare the Device Token for Registration (remove spaces and < >)
    	NSString *deviceToken = [[[[devToken description]
                                   stringByReplacingOccurrencesOfString:@"<"withString:@""]
                                  stringByReplacingOccurrencesOfString:@">" withString:@""]
                                 stringByReplacingOccurrencesOfString: @" " withString: @""];
    
    	// Build URL String for Registration
    	// !!! CHANGE "www.mywebsite.com" TO YOUR WEBSITE. Leave out the http://
    	// !!! SAMPLE: "secure.awesomeapp.com"
    	NSString *host = @"www.example.com/apns";
    
    	// !!! CHANGE "/apns.php?" TO THE PATH TO WHERE apns.php IS INSTALLED
    	// !!! ( MUST START WITH / AND END WITH ? ).
    	// !!! SAMPLE: "/path/to/apns.php?"
    	NSString *urlString = [@"/apns.php?"stringByAppendingString:@"task=register"];
    
    	urlString = [urlString stringByAppendingString:@"&appname="];
    	urlString = [urlString stringByAppendingString:appName];
    	urlString = [urlString stringByAppendingString:@"&appversion="];
    	urlString = [urlString stringByAppendingString:appVersion];
    	urlString = [urlString stringByAppendingString:@"&deviceuid="];
    	urlString = [urlString stringByAppendingString:deviceUuid];
    	urlString = [urlString stringByAppendingString:@"&devicetoken="];
    	urlString = [urlString stringByAppendingString:deviceToken];
    	urlString = [urlString stringByAppendingString:@"&devicename="];
    	urlString = [urlString stringByAppendingString:deviceName];
    	urlString = [urlString stringByAppendingString:@"&devicemodel="];
    	urlString = [urlString stringByAppendingString:deviceModel];
    	urlString = [urlString stringByAppendingString:@"&deviceversion="];
    	urlString = [urlString stringByAppendingString:deviceSystemVersion];
    	urlString = [urlString stringByAppendingString:@"&pushbadge="];
    	urlString = [urlString stringByAppendingString:pushBadge];
    	urlString = [urlString stringByAppendingString:@"&pushalert="];
    	urlString = [urlString stringByAppendingString:pushAlert];
    	urlString = [urlString stringByAppendingString:@"&pushsound="];
    	urlString = [urlString stringByAppendingString:pushSound];
    
    	// Register the Device Data
    	// !!! CHANGE "http" TO "https" IF YOU ARE USING HTTPS PROTOCOL
    	NSURL *url = [[NSURL alloc] initWithScheme:@"http" host:host path:urlString];
        NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
    	NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];
    	NSLog(@"Register URL: %@", url);
    	NSLog(@"Return Data: %@", returnData);
    
        /* EasyAPNS End Block */
    
        /* PushNotification Start Block */
        PushNotification* pushHandler = [self.viewController getCommandInstance:@"PushNotification"];
        [pushHandler didRegisterForRemoteNotificationsWithDeviceToken:devToken];
        /* PushNotification End Block */
    
    #endif
    }
    
    - (void)application:(UIApplication*)app didFailToRegisterForRemoteNotificationsWithError:(NSError*)error
    {
        PushNotification* pushHandler = [self.viewController getCommandInstance:@"PushNotification"];
        [pushHandler didFailToRegisterForRemoteNotificationsWithError:error];
    }
    
    - (void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo
    {
        PushNotification* pushHandler = [self.viewController getCommandInstance:@"PushNotification"];
        NSMutableDictionary* mutableUserInfo = [userInfo mutableCopy];
    
        // Get application state for iOS4.x+ devices, otherwise assume active
        UIApplicationState appState = UIApplicationStateActive;
        if ([application respondsToSelector:@selector(applicationState)]) {
            appState = application.applicationState;
        }
    
        [mutableUserInfo setValue:@"0" forKey:@"applicationLaunchNotification"];
        if (appState == UIApplicationStateActive) {
            [mutableUserInfo setValue:@"1" forKey:@"applicationStateActive"];
            [pushHandler didReceiveRemoteNotification:mutableUserInfo];
        } else {
            [mutableUserInfo setValue:@"0" forKey:@"applicationStateActive"];
            [mutableUserInfo setValue:[NSNumber numberWithDouble: [[NSDate date] timeIntervalSince1970]] forKey:@"timestamp"];
            [pushHandler.pendingNotifications addObject:mutableUserInfo];
        }
    }
    
    /* STOP BLOCK */
    
    @end

13. Build 在你的 device 上吧, 在emulator 沒辦法測 notification.

14. EasyAPNS Server Side 的部份, 我就沒寫了, 請參考下方 1j2 的連結即可

PS:
1.
這只是我個人的筆記, 寫的有點雜亂 XD
所以請搭配下列 ref 一併服用
未來還會再整理一遍, 目前只是先備忘
2.
產生 .pem
openssl pkcs12 -in Dev_Certificates.p12 -out apns-dev-cert.pem -nodes -clcerts

ref:
http://1j2.com/tutorial-implementing-push-notifications-with-easy-apns/
https://github.com/phonegap/phonegap-plugins/tree/master/iOS/PushNotification
http://www.easyapns.com/apple-delegate

Zend Framework Widget

主要是因為之前用Yii 的 Widget 用的挺開心的( 除了他會每次都進一次Cycle 外)

因為用ZF沒找到有這方面相關的資訊
所以自已動手刻…..

1. 在application 下建 Titan/Base 的資料夾 ( Titan 是我想把自已寫的東西盡可能的都收在這 )
2. vim Titan/Base/Widget.php
[sourcecode language=’php’]
< ?php abstract class Titan_Base_Widget { protected $_basePath; protected $_viewBasePath; protected $_viewScriptPath; public function __construct( $array = null ) { //assign variables $this->_basePath = APPLICATION_PATH . DS . ‘widgets’;
$this->_viewBasePath = APPLICATION_PATH . DS . ‘widgets’ . DS . ‘views’;
$this->_viewScriptPath = $this->_viewBasePath . DS . ‘scripts’;
$this->_config = Zend_Registry::get( ‘config’ );

if ( !empty( $array ) )
{
foreach( $array as $key => $val )
{
$this->$key = $val;
}
}

try {
$this->preWidget();
$this->run();
$this->postWidget();
} catch ( Zend_Exception $e ) {
echo “Caught exception: ” . get_class($e) . “n”;
echo “Message: ” . $e->getMessage() . “n”;
}

}

public function render( $action )
{
include( $this->_viewScriptPath . DS . $action . DS . $action . $this->_config->resources->view->suffix );
}

protected function preWidget(){}
abstract protected function run();
protected function postWidget(){}
}
[/sourcecode]

3. 加入include_path
[sourcecode language=’php’]
realpath(APPLICATION_PATH . ‘/widgets/controllers’ ),
realpath(APPLICATION_PATH ),
[/sourcecode]

4. 在application 目錄下建 widgets/controllers, widgets/views/scripts ( views這樣的目錄結構是因為保留到時後用Zend_View的彈性 )

5. login widget controller
[sourcecode language=’php’]
< ?php class LoginWidget extends Titan_Base_Widget { protected function run() { $this->render( ‘login’ );
}
}
[/sourcecode]

6. login widget view
在 widgets/views/scripts 下建一個 login 目錄
寫一個login.phtml
[sourcecode language=’php’]
< ?= $this->okok; ?>
[/sourcecode]

7. index view
[sourcecode language=’php’]
< ?php new LoginWidget( array( 'okok' => ‘ok1ok’ ) ); ?>
[/sourcecode]

這樣就搞定了…

PS
1.
原本想用Zend_View 來實做的
不過搞了一陣子..在他的ob_start() 及 response->appendBody 花了一些時間弄了滿久的(有一些時間壓力)
整個流程還不是搞的相當懂, 在不想把整個架構弄的很醜的情況下, 目前先用這種方式實作

2. 話說我好像一陣子沒寫筆記了………………..

Yii 1.1 CActiveRecord關連的部份修正

剛剛用Yii 的AR 寫的很開心,
下了這個
[sourcecode language=’php’]
$criteria=new CDbCriteria;
$criteria->condition=’products.id=:ID AND products_info.language_id=:languageID’;
$criteria->params=array(‘:ID’=>$_GET[‘id’], ‘:languageID’ => $this->_lang_id );
$criteria->order = ‘products_content.content_orders ASC’;

$model = Products::model()->with( ‘products_info’, ‘products_content’ )->find( $criteria );
[/sourcecode]
一直出現
CDbCommand 無法執行 SQL 陳述: SQLSTATE[42S22]: Column not found: 1054 Unknown column ‘products.id’ in ‘where clause’
想說怪了…
再看了一下 sql log
[sourcecode language=’text’]
543 Query SELECT `t`.`id` AS `t0_c0`, `products_info`.`products_id` AS `t1_c0`, `products_info`.`language_id` AS `t1_c1`, `products_info`.`name` AS `t1_c2`, `products_info`.`content` AS `t1_c3`, `products_content`.`id` AS `t2_c0`, `products_content`.`products_id` AS `t2_c1`, `products_content`.`language_id` AS `t2_c2`, `products_content`.`content_name` AS `t2_c3`, `products_content`.`content_value` AS `t2_c4`, `products_content`.`content_orders` AS `t2_c5` FROM `products` `t` LEFT OUTER JOIN `products_info` `products_info` ON (`products_info`.`products_id`=`t`.`id`) LEFT OUTER JOIN `products_content` `products_content` ON (`products_content`.`products_id`=`t`.`id`) WHERE (products.id=’1′ AND products_info.language_id=’1′) ORDER BY products_content.content_orders ASC
[/sourcecode]

發現Products model 變成t 了
想說很怪
再去看了一下 1.1 CHANGELOG
發現了這段..
The alias name for the primary table in a relational AR query is changed to be ‘t’ (Qiang)

好吧乖乖的把products.id改成t.id

Yii Csort With ActiveRecord

controller 部份
[sourcecode language=’php’]
$sort->attributes= array(
‘product.id’=>’productId’,
‘product.name’=>’productName’,
‘category.name’=>’categoryName’,
‘brand.name’ => ‘brandName’,
);

/*
$sort->attributes= array(
‘model中的alias.欄位’=>’網址顯示的Alias’,
);
*/
[/sourcecode]

view 部份
[sourcecode language=’php’]
< ?php echo $sort->link(‘product.id’,’編號’); ?>
//< ?php echo $sort->link(‘table.name’,’網頁顯示的名稱’); ?>
[/sourcecode]

model 部份
記得把 relations中的 alias 設好

如下
[sourcecode language=’php’]
/**
* @return array relational rules.
*/
public function relations()
{
return array(
‘brand’=>array(self::BELONGS_TO,’brand’,’brand_id’, ‘alias’=>’brand’),
‘category’=>array(self::BELONGS_TO,’category’,’category_id’, ‘alias’=>’category’),
‘series’=>array(self::BELONGS_TO,’series’,’series_id’,’alias’=>’series’),
);
}
[/sourcecode]

system()&exec()

system()
The system() call also tries to automatically flush the web server’s output buffer after each line of output if PHP is running as a server module.
exec()
則不會flush

遇到的問題是
我的php 透過perl 去執行一些功能
然後perl 回傳 success or false 字串 在standard output 上
php再去get 這個字串
success 則print success
給 ajax 來判斷

結果 在前端的ajax 一直印出 successsuccess
的字眼
想說怎麼會印兩次….
看了一下手冊才發現這個

之前就想說 php 幹麻沒事有兩個 執行CLI 的東西……
仔細看了一下手冊才知道
筆記一下..

YUI RTE setEditorHTML….

[sourcecode language=’js’]
var myEditor = new YAHOO.widget.Editor(‘editor’, config);
myEditor.on(‘editorContentLoaded’, function() {
myEditor.setEditorHTML(‘This is my new content’);
});
myEditor.render();
[/sourcecode]

要 listening editorContentLoaded event 才行
沒在editorContentLoaded 裡下………….
myEditor.setEditorHTML(‘This is my new content’);
沒作用………

some YUI RTE note..

在<body> 這個tag 似乎一定要設
class=’yui-skin-sam’
不然會有一些小問題
我之前是設在<div class=’yui-skin-sam’><textarea></textarea></div>
這樣來解 ..不過…
用了一陣子才發現….class=’yui-skin-sam’不是設在body 的話….
add link 的這個func 會破版…………..css 會整個走掉….

本來是沒有很想加 class 在body 的..
不過突然想到….drupal 的 plugin 中也有yui rte
我就去看了一下他是怎麼解的..
[sourcecode language=’js’]

[/sourcecode]

然後看了一下skin裡面的css 應該是不至於加了這個class 造成新版的走位,所以就照著幹了…

YUI Rich Text Editor小記

因為有打算用YUI RTE 來做default 的editor
不過因為有想要在textarea 裡面加入content 的須求
用document.post_form.post_text.value
會沒辦法在”所見即所得模式”下有反應
只能在原始碼模式下有作用

翻了一下文件
要用cmd_inserthtml來做
如下
[sourcecode language=’js’]
myEditor = YAHOO.widget.EditorInfo.getEditorById(‘post_text’);
myEditor.execCommand(‘inserthtml’, document.post_form.post_text.value);
[/sourcecode]
這樣應該就搞定了…