sugarCrm翻译 及 文件内容排序后保存,避免冲突过多

Logic Hook

hook配置信息和触发器定义在以下目录中 

./custom/Extension/modules/<module>/Ext/LogicHooks/<file>.php

./custom/modules/<module>/logic_hooks.php(避免使用)

./custom/Extension/application/Ext/LogicHooks/<file>.php

./custom/modules/logic_hooks.php(避免使用)

具体的hook代码在配置文件中制定, 但是建议放在与之相关的module目录中去

配置文件还定义了执行hook的类名和方法名

可以通过代码调用去移除hook: remove_logic_hook("<module>", "before_save", $my_hook);

为防止发生无限循环执行hook的情况, 可以在类里加一个属性, 来记录是否已经执行过hook, 从而避免不必要的hook执行

hook分类:

db保存前, 保存后

数据删除前, 后 ..

登录, 退出: 成功/失败

job执行失败

job执行失败后重试

数据展示到Listview或Editview前进行处理

Application Hooks
  after_ui_frame
  after_ui_footer
  server_round_trip
  after_delete
  after_relationship_add
  after_relationship_delete
  after_restore
  after_retrieve
  after_save
  before_delete
  before_restore
  before_save
  process_record
Job Queue Hooks
  job_failure
  job_failure_retry
User Hooks
  after_load_user
  after_login
  after_logout
  before_logout
  login_failed

Sugar Logic(逻辑运算/数学运算??)

方便自定义的业务逻辑代码的创建, 管理以及在服务器端和客户端重用

他由许多互相独立和可扩展的组件构成, 其中的一个重要组件就是 Sugar Formula 引擎, 它用来解析和求值那些人类读懂的数学公式

Formula 是一个代码表达式, 包含了嵌套的函数和变量, 符合 Sugar Formula 引擎语法, 用来实现比如 加法, 乘法, 逻辑运算: add(1, 2);  not(equal($billing_state, "CA")); multiply(number($employees), $seat_cost, 0.0833); ...

Function 可以被调用的函数, 他的输出结果无论在服务端还是前段都是一样的

Trigger 一个只返回true或false的Formula, 他在这种情况下被执行: 当一个公式中的字段被更新或者一条记录被获取/保存, 此时他会通知关联的action去执行一些动作

Action 以某种方法去更改当前记录或者布局的函数, 大多数有两个参数, 一个目标, 一个formula; 比如说 style action(样式动作), 需要传递一个字段, 和一个字符串样式, 就可以用传入的样式来更改字段的样式; 列表

Dependencies  是由触发器和action组成的用来表达业务逻辑的单元, 比如: 当一个下拉选项中的值没有被选中的时候, 对应的面板要隐藏起来

Sugar Logic Types (类型)

number 数值类型, string 字符串类型, enum 枚举类型(列表类型) link 连接类型(描述两个表之间的关联关系)

Sugar Logic 基本特性(Sugar Logic Based Features)

计算字段的值, 如显示的时候, 将值乘以0.1而不是原值显示

字段关联, 比如当一个字段值在某一个范围的时候才在页面中显示出来

下拉列表关联, 如多级联动效果

自定义

自定义Function,

他将会存放在/custom/include/Expressions/Expression/{Type}/{Function_Name}.php

文件名必须是: {functionName}Expression.php, 类名跟文件名要一样, 例如继承了数字类型的function: class AbcExpression extends NumericExpression{}

必须要同时定义php和JavaScript两个相同的计算逻辑,  当在admin面板中操作"Rebuild Sugar Logic Functions"重新构建时, 相关的js代码(代码文件??)就会被编译出来

自定义Action

自定义的action就不用像function那样保持前后端一致了, 比如, 填写表单时用户输入错误了, 前段会有一个alert提醒, 但是后端会记录一条日志

他会存放在: custom/include/Expressions/Actions/{ActionName}.php ,

文件名必须以Action.php结尾, 类名跟文件名要一样, 同时得继承AbstractAction类: class WarningAction extends AbstractAction{}

必须定义一些基本的方法: "fire", "getDefinition", "getActionName", "getJavascriptClass",  "getJavscriptFire"

Extensions 扩展

扩展的目的是为了提供一个修改Sugar metadata(vardefs and layouts)的渠道,

他的文件存放在: ./custom/Extension/application/Ext/ 和 ./custom/Extension/modules/<module_name>/Ext 目录下

这些文件会通过一个事先定义好的规则(例如: vardef.ext.php)被合并成一个单独的文件,

比如vardefs文件, ./custom/Extension/modules/Accounts/Ext/Vardefs/ 目录下的所有文件都会被合并, 里边的变量合并成一个数组放到./custom/modules/Accounts/Ext/Vardefs/vardefs.ext.php中, 其中:

如果你通过studio添加了一个test字段, 就会在./custom/Extension/modules/Accounts/Ext/Vardefs/ 目录下生成一个 sugarfield_test_c.php文件, 里边很简单就一行: $dictionary['Task']['fields']['test_c']['labelValue']='test';

./ModuleInstall/extensions.php 包含了所有的扩展映射关系

扩展的属性:

1. name: 扩展的内部名字, This is used in method names such as rebuild_layouts (???)

2. install definition: manifest文件的段的名字

3. ext directory : 扩展文件存放的目录

4. ext file: 合并后存放的文件名

5. useage: 扩展在哪里被用到了

扩展种类:

 ActionFileMap  如果你不想把视图文件放在./custom/modules/<module>/views/view.<name>.php, 这个文件定义了action到文件的映射关系   
 ActionReMap  将一个action映射到另一个已经存在的action  
 ActionViewMap  给module添加一个action映射, 通常  
 Administration  在admin页面添加一个管理面板 (Used to add new administrative panels to the admin section)  
 Dependencies  给字段或者表单添加一个依赖(关联)的行为去处理一些复杂的逻辑(studio界面暂时不能用)  
 EntryPointRegistry  添加额外的入口映射  
 Extensions  允许用户在框架内自定义扩展, 他将跟./ModuleInstaller/extensions.php中的扩展一起使用  
 FileAccessControlMap  用于限制系统用户的特定视图操作 (Used to restrict specific view actions from users of the system. 限定系统用户可以访问哪些页面??)  
 GlobalLinks  定义全局可用的超链接  
 Include  用来映射系统中额外的modules, 通常是构建工具利用他来生成一个module  
 JSGroupings  添加js组(Used to add additional JavaScript groupings the system ??)  
 Language  他可以添加或者覆盖已有的语言描述, 相关的application 和 module 目录都能用到  
 Layoutdefs  用来添加或者覆盖子面板的定义  
 LogicHooks  给某一个action添加一个特定的功能, 比如在保存数据之前  
 Menus  管理module的菜单, (会覆盖标准的菜单, 所以要全部重新定义)  
 ScheduledTasks  自定义计划任务  
 UserPage  在用户管理详情页添加一个章节section  
 Utils  给工全局的具包(util)添加功能函数  
 vardefs  添加或者覆盖vardefs  
 WirelessLayoutdefs  给移动端视图(mobile)添加一个子面板  
 WirelessModuleRegistry  给移动端添加额外的modules  

注意: 

通过 SugarCRM 工作室添加了模块或字段后, sugar 会重新生成并覆盖一大堆的配置文件, 而且是新生成的文件内容是无序的,

这就出现一个问题, 当小组多个成员一起开发时, 这种无序会导致冲突, 很多很大的冲突,

这就需要改动sugar的框架文件, 让重新生成配置文件时, 以字母有序的方式排列内容, (注意有些文件比如布局文件, 就不能进行排序了)

include/utils/array_utils.php

1 function var_export_helper($tempArray, $isSort=TRUE) {
2     if ($isSort && is_array($tempArray)) {
3         ksort($tempArray, SORT_NATURAL | SORT_FLAG_CASE); //按照字符顺序a-z排序, 不区分大小写
4     }
5     
6     return var_export($tempArray, true);
7 }
View Code

 

include/utils/file_utils.php

 1 function write_array_to_file( $the_name, $the_array, $the_file, $mode="w", $header='' )
 2 {
 3     if(!empty($header) && ($mode != 'a' || !file_exists($the_file))){
 4         $the_string = $header;
 5     }else{
 6         $the_string =   "<?php\n" .
 7                     '// created: ' . date('Y-m-d H:i:s') . "\n";
 8     }
 9     $tmp = array();
10     if (strpos($the_file, 'subpanel') !== FALSE) {
11         $tmp = var_export_helper( $the_array, FALSE);
12     } else {
13         $tmp = var_export_helper( $the_array );
14     }
15     $the_string .=  "\$$the_name = " . $tmp . ";";
16     return sugar_file_put_contents($the_file, $the_string, LOCK_EX) !== false;
17 }
View Code

 

modules/Administration/Common.php

function save_custom_app_list_strings_contents(&$contents, $language, $custom_dir_name = ''){

......
if($dir_exists)
   {
      $filename = "$dirname/$language.lang.php";
      $handle = @sugar_fopen($filename, 'w');

      if($handle)
      {
           $contents = sortAppListStringContent($contents); //添加此行函数调用
         if(fwrite($handle, $contents))
         {
            $return_value = true;
            $GLOBALS['log']->info("Successful write to: $filename");
         }

         fclose($handle);
      }
      else
      {
         $GLOBALS['log']->info("Unable to write edited  language pak to file: $filename");

.......

}


/**
  * 将下拉列表的内容排序后返回
  * @param $contents
  * @return string
  */
function sortAppListStringContent($contents)
{
    $rn = PHP_EOL;
    //匹配 $app_strings 导航栏
    preg_match_all('#\$app_strings\[\'(.*)\'\]\s*=.*;#U', $contents, $matches);
    $list0 = array();
    if (!empty($matches[0])) {
        foreach($matches[0] as $k => $content) {
            $key = $matches[1][$k];
            $list0[$key] = $content;
        }
        ksort($list0, SORT_NATURAL | SORT_FLAG_CASE); //排序, 方便查看更改内容
    }
    $strList0 = implode($rn, array_values($list0));
    
    //匹配 $app_list_strings
    preg_match_all('#\$app_list_strings\[\'(.*)\'\]\[\'(.*)\'\]\s*=.*;#U', $contents, $matches);
    
    $list1 = array();
    if (!empty($matches[0])) {
        foreach($matches[0] as $k => $content) {
            $key = $matches[1][$k].'_'.$matches[2][$k];
            $list1[$key] = $content;
        }
        ksort($list1, SORT_NATURAL | SORT_FLAG_CASE); //排序, 方便查看更改内容
    }
    $strList1 = implode($rn, array_values($list1));
    
    //匹配 $GLOBALS['app_list_strings']['xxx']
    preg_match_all('#\$GLOBALS\[\'app_list_strings\']\[\'(.*)\'\].*;#Us', $contents, $matches);
    
    $list2 = array();
    if (!empty($matches[0])) {
        foreach($matches[0] as $k => $content) {
            $key = $matches[1][$k];
            $content = preg_replace('#\s+#s', ' ', $content);
            $content = preg_replace('#array\s*\(\s*#', 'array ('.$rn.'  ', $content);
            $content = preg_replace('#,\s*#', ','.$rn.'  ', $content);
            $content = preg_replace('#\s*\);#', $rn.');', $content);
            $list2[$key] = $content;
            
        }
        ksort($list2, SORT_NATURAL | SORT_FLAG_CASE); //排序, 方便查看更改内容
    }
    $strList2 = implode($rn, array_values($list2));
    
    //$list2[$dropdown_name] = "\$GLOBALS['app_list_strings']['{$dropdown_name}']=".var_export_helper($dropdown).';';
    
    
    $string = '<?php' . $rn.
        $strList0. $rn.
        $strList1. $rn.
        $strList2. $rn;
    
    return $string;
}
View Code

 

modules/ModuleBuilder/parsers/parser.dropdown.php

1 function getNewCustomContents($dropdown_name, $dropdown, $lang) {
2         $contents = return_custom_app_list_strings_file_contents($lang);
3         $contents = str_replace("?>", '', $contents);
4         if(empty($contents))$contents = "<?php";
5         $contents = preg_replace($this->getPatternMatch($dropdown_name), "\n", $contents);
6         $contents .= "\n\$GLOBALS['app_list_strings']['$dropdown_name']=" . var_export_helper($dropdown, FALSE) . ";";
7         return $contents;
8     }
View Code

 

modules/ModuleBuilder/parsers/views/AbstractMetaDataImplementation.php

 1 protected function _saveToFile ($filename , $defs , $useVariables = true, $forPopup = false )
 2     {
 3         .....
 4 if($forPopup){
 5             $out .= "\$$viewVariable = \n" . var_export_helper ( $defs, FALSE) ;
 6         }else{
 7             $out .= "\$$viewVariable [".(($useVariables) ? '$module_name' : "'$this->_moduleName'")."] = \n" . var_export_helper ( $defs , FALSE) ;
 8         }
 9 .....
10 }
View Code

 

modules/ModuleBuilder/parsers/views/PopupMetaDataParser.php

 1 function handleSave ($populate = true)
 2 {
 3 ......
 4 foreach( $allProperties as $p){
 5             if(isset($popupMeta[$p])){
 6                 $out .= "    '$p' => ". var_export_helper ($popupMeta[$p], FALSE) . ",\n";
 7             }
 8         }
 9 ......
10 }
View Code

 

posted @ 2018-03-15 13:36  myD  阅读(431)  评论(0编辑  收藏  举报