2020年10月29日 星期四

CCS的權限設計很好用的

 CodeCharge的權限設計,分為三個等級:

1. 全站

2. 全頁(包含子頁)

3. 頁裡面的 Form Record/Grid 元件

--------------------------------------------------------

應用:不同會員等級,看見不同的選單MENU

設計整站會員等級。不同等級有不同的權限。
使用Group_level這個欄位,在Project Setting 裡面有這個選項。

選單頁中,採用可以被引入的 include page方式。

一般頁 + 選單子頁

選單子頁 再 + 子選單子頁

子選單子頁 再 分別設定會員等級權限

CCS就會依據會員等級權限,只顯示有權限的子頁



---------------------------------------------------------------

之前自己使用某頁來做測試,後來自己忘記了,竟然發現怎麼跑不出來了!經過好幾個小時的測試,還是跑不出來!最後,才發現,原來是自己更改了權限設計測試資料,才跑不出來。將權限設定拿掉就正常了!

這 CodeCharge的權限也太厲害了,搞到自己都暈了!

自覺這是一個糗事,紀錄一下,提醒自己,以後不要再發生!



2020年10月27日 星期二

談 CodeChargeStudio 的 upload 問題

原始的CCS文件:Description

File Upload 元件,使用來上傳到指定的 Web server.
使用 (<input type="file">) 讓使用者在他自己的電腦上瀏覽選擇檔案。
用戶選擇、且按了送出鍵,就上傳。
File Upload 元件會檢查 檔案大小限制 file size limit。
同時檢查允許的檔案格式副檔名。
成功上傳到 server (註一)以後,CCS就會給他一個檔名"yyyymmddHHnnss#.<originalName>",
'yyyymmddHHnnss' 是當時系統日期時間。
#加上序號,確保名稱唯一性。
File Upload 必須在 Record form 或 Editable Grid。


CCS的 upload control 必須在 Record/Grid 裡面使用。

使用 html 的 <input type="file"> 來做上傳。

會在 HTML 產生這段碼:

<!-- BEGIN FileUpload name -->
  <input type="hidden" name="{ControlName}" value="{State}">
  <!-- BEGIN Info --> {FileName} {FileSize} bytes <!-- END Info -->
  <!-- BEGIN Upload --><input type="file" name="{FileControl}"><!-- END Upload -->
  <!-- BEGIN DeleteControl --> Delete <input type="checkbox" name="{DeleteControl}" {DeleteChecked}><!-- END DeleteControl -->
<!-- END FileUpload name -->

File Input Control

File Upload 元件裡面主要的元素是 file input control. HTML 碼如下:

<input type= "file" name= "{ControlName}">

這個是讓用戶可以在他的電腦上瀏覽選取檔案。

檔案選擇以後,送出鍵,就會上傳到 server。(這一段:註一,由PHP處理)
在儲存到主機之前,CCS會檢查檔案大小以及格式,如果有錯,就會產生錯誤訊息,檔案不會儲存。

Note that the file input control appears when a new record is being inserted or there is no existing file associated with the record being viewed. If the record being viewed has an existing file, the File Upload component will display information about the file and hide the file input control. 


* 研究CCS的程式碼以後(包含:主程式.php、common.php、classes.php、還有 CCS所附的sample2) 發現,配合 upload 一定要配合一個 uploaded files 的  資料庫  table,例如:files,然後在該 upload control的 property裡面綁定的 table name、field等綁定,才能夠真正正確的執行整個CCS所提供的功能。如果沒有綁定table、field等,好像就是上傳到 Temporary Folder,無法再搬移到 File Folder裡。因為搬移這個動作,是設計在 Record form裡面的 insert()之後動作的!

這一點,我試了好久,沒有綁定table、一直試 File Folder,就是無用!只有在 Temporary Folder而已!只好去K CCS的程式碼、找 Sample2裡面的程式,才發現這樣!

* 接下來,我要解決中文檔名的問題。因為上傳中文檔名,在 php的 move_uploaded_file()這裡會有錯誤。我們無法去改 php,只有自己想辦法解決!

move_uploaded_file(),只能接受 big5檔案名稱,所以,在使用以前,必須將utf8的中文名稱轉換成 big5,才可以呼叫 move_uploaded_file()

修改: classes.php

line 1839:
while($file_exists) {
// $ActualFileName = date("YmdHis") . $index . "." . $FileName;
$ActualFileName = date("YmdHis") . $index . "." . iconv("utf-8", "big5", $FileName);
$file_exists = file_exists($this->FileFolder . $ActualFileName) || file_exists($this->TemporaryFolder . $ActualFileName);
$index++;
}
if( move_uploaded_file($_FILES[$FileControl]["tmp_name"], $this->TemporaryFolder . $ActualFileName)) {
...
(略)
...
}

主要是 將 $FileName 用 iconv("utf-8","big5",$FileName)轉換成 big5

然後,交由 move_uploaded_file()處理就可以

CCS 將上傳的檔案(記住:中文檔名為 big5碼的)存放到 設定的 TemporaryFolder 裡面。

然後、綁定的TABLE新增一筆紀錄,此時要將檔名由 big5轉回 utf-8,才能正確儲存。

試了整個晚上,終於試出來了!就是在 upload 綁定的 record form 的Event: fileForm_ds_BeforeExecuteInsert 裡面,直接去修改 INSERT 的 SQL ,整個改掉,改成:

$fileForm->DataSource->SQL = 
"INSERT INTO files  (file_name, file_date_uploaded, file_owner_id, file_status) VALUES( " .
CCToSQL( iconv("big5","utf-8",$fileForm->FileUpload1->GetValue()), ccsText) . ",".
 " NOW() " . "," .
CCToSQL( $fileForm->file_owner_id->GetValue(), ccsInteger) . "," .
CCToSQL( $fileForm->file_status->GetValue(), ccsInteger) . ")"; 

主要要點是:將 file_name,使用 iconv("big5", "utf-8", filename欄位) 改回 utf-8

這樣就完成了。

總結:

未來:1. Classes.php 要注意使用這個修改一行的版本。本來想要盡量不要動到CCS預設的 Classes.php檔案,但是試了幾天,都找不到方法可以不動。那就只好動它了!記得維持這個版本。萬一,這版本被CCS改回去了。就記得手動去改 1840 那一行。

2. upload control 所在的 網頁檔,記得要綁定一個 Record / Editable Grid,才可以使用 upload 元件,同時,是先定義好files  table,裡面的欄位 filename 等。然後,在該 record 的 BeforeExecuteInsert Event 去改掉 INSERT SQL ,將 中文檔名編碼格式由 big5 改回 utf-8,如上。(去 copy來改比較快啦!)

完成!


註一:php 原始上傳是先上傳到 php.ini 裡面定義的 tmp folder,然後紀錄在$_FILE["file"] 系統變數裡面

分別是:

$_FILES["file"]["name"] - the name of the uploaded file
$_FILES["file"]["type"] - the type of the uploaded file
$_FILES["file"]["size"] - the size in bytes of the uploaded file
$_FILES["file"]["tmp_name"] - the name of the temporary copy of the file stored on the server
$_FILES["file"]["error"] - the error code resulting from the file upload

然後:再呼叫 move_uploaded_file($_FILES["file"]["tmp_name"],  $_FILES["file"]["name"]);

2. CCS 的 upload control 可以設定 兩個 property: Temporary Folder、 File Folder。


註二:2020/10/28 中午,將以上昨晚在local 電腦(windows系統)測試都成功的東東,上傳到 Linux Server!結果發現:又錯了!問題就在於 Windows 的中文檔名,與 Linux 的中文檔名,編譯的 碼 不一樣!

結果,我試著將 昨天以前在 windows 上測試好幾天成功的東東都拿掉,還原回原本CCS上的原始碼狀態,竟然成功運作!哈哈哈!也就是說我這幾天都做白工了!

唉!耶沒有做白工啦!至少對CCS的內部設計,更清楚一些了。也興起我運用他來設計一套 Generator 的想法!

哈哈!功不唐捐啦!~~~


2020年10月26日 星期一

mysql unique index null 不計

 mysql 裡面,unique index key如果含有 null,他並未計入唯一的檢查。還是可以接受兩筆以上的 null資料。

如果希望不能兩筆null資料,那就只好將 null 資料,改為 ""、空字串,這樣就可以辨識唯一了。


CREATE TABLE table1 (x INT NULL UNIQUE);
INSERT table1 VALUES (1);
INSERT table1 VALUES (1);   -- Duplicate entry '1' for key 'x'
INSERT table1 VALUES (NULL);
INSERT table1 VALUES (NULL);
SELECT * FROM table1;

Insert Into Table(A, B) Values (null, 2);
Insert Into Table(A, B) Values (null, 2);#should fail do to duplicate values
這種情形,mysql 還是允許。
若改成"",就可以由Index來限制唯一了!
Insert Into Table(A, B) Values ("", 2);
Insert Into Table(A, B) Values ("", 2);#should fail do to duplicate values
記得:這樣一來,使用程式在做判斷時,就要使用  empty()來判斷


2020年10月20日 星期二

使用CloudFlare,來源IP會改變,這裡是用法

CloudFlare 會額外增加SERVER Variable變數,如下: 

$_SERVER["HTTP_CF_CONNECTING_IP"] real visitor ip address, this is what you want

$_SERVER["HTTP_CF_IPCOUNTRY"] country of visitor

$_SERVER["HTTP_CF_RAY"]

$_SERVER["HTTP_CF_VISITOR"] this can help you know if its http or https


you can use it like this:

if (isset($_SERVER["HTTP_CF_CONNECTING_IP"])) {

  $_SERVER['REMOTE_ADDR'] = $_SERVER["HTTP_CF_CONNECTING_IP"];

}

If you do this, and the validity of the visiting IP address is important, you might need to verify that the $_SERVER["REMOTE_ADDR"] contains an actual valid cloudflare IP address, because anyone can fake the header if he was able to connect directly to the server IP.


參考網址:

https://stackoverflow.com/questions/14985518/cloudflare-and-logging-visitor-ip-addresses-via-in-php

https://support.cloudflare.com/hc/en-us/articles/200170786-Restoring-original-visitor-IPs-Logging-visitor-IP-addresses

https://support.cloudflare.com/hc/en-us/articles/200170986-How-does-Cloudflare-handle-HTTP-Request-headers-


但是我使用$_SERVER["HTTP_CF_CONNECTING_IP"] 發現得出來的 IP是:2001:b011:3009:17b8:8871:298c:88d7:9104,不是IPV4格式

這還要研究一下。

我現在暫時修改了 common.php 裡面的

if (isset($_SERVER["HTTP_CF_CONNECTING_IP"])) {

  $_SERVER['REMOTE_ADDR'] = $_SERVER["HTTP_CF_CONNECTING_IP"];

}

if (CCGetUserAddr() != $_SERVER["REMOTE_ADDR"]) { CCLogoutUser(); }




--------------------

另外:

PHP 取得用戶真實 IP

https://devco.re/blog/2014/06/19/client-ip-detection/
https://ithelp.ithome.com.tw/articles/10216399



<?php
if (!empty($_SERVER["HTTP_CLIENT_IP"])){
    $ip = $_SERVER["HTTP_CLIENT_IP"];
}elseif(!empty($_SERVER["HTTP_X_FORWARDED_FOR"])){
    $ip = $_SERVER["HTTP_X_FORWARDED_FOR"];
}else{
    $ip = $_SERVER["REMOTE_ADDR"];
}
    
echo $ip;
?>


可以參考的 HTTP Header(依照可能存放真實 IP 的順序)

  • HTTP_CLIENT_IP
  • HTTP_X_FORWARDED_FOR
  • HTTP_X_FORWARDED
  • HTTP_X_CLUSTER_CLIENT_IP
  • HTTP_FORWARDED_FOR
  • HTTP_FORWARDED
  • REMOTE_ADDR (真實 IP 或是 Proxy IP)
  • HTTP_VIA (參考經過的 Proxy)




2020年10月19日 星期一

CodeCharge 的 field validation

在 Field 的 property 裡面有 Validation rule、以及 Validation Text 兩個

舉例:Login 登入帳號,要求長度需要大於 8 位元,就在該欄位的 Validation rule 輸入:strlen($this->login->GetValue())>=8

Validation TEXT:{res:msg_login_length_must_be_greater_than_8}

然後在 多語系定義裡面,定義上面那段{res:msg_login_length_must_be_greater_than_8}就可以了!


Code Charge 的 Validation 可以在很多地方處理:

一、在欄位的屬性裡

1. 是否 Unique、是否 Required、 Input Validation格式(EMail、5-digit ZIP Codes)

2. 就如同本文上面所舉的例子

3. Error Control:將錯誤訊息顯示在一個 Label 裡面,該Label Control 可以隨自己控制,放在想要顯示的地方。

標準的 Error是顯示在畫面的 上面。

二、每個欄位、Control、Page都有 OnValid_Event

可在那個 Event 裡面寫程式檢查


如何判斷現在FORM是在 insert mode? 還是 update mode?

只要用  if (empty({primary_key})) 就可以知道是否為新增模式了。 如果 {promary_key} 是空白的,那麼就是在新增模式;反之,就是更新模式。 以上。