読者です 読者をやめる 読者になる 読者になる

Snapshot原田

備忘録

List.js

上から読み込まれた順に記述。案件をクリックして、リストビューのページにとんだ時

ListViewの画面を読み込んだ時に走る

  • getListViewContentContainer リストビューページのコンテンツ全体を包むコンテナdivの中身を読み込んで  class="listViewContentDiv"を含むdivをオブジェクトとして返す

  • registerPageNavigationEvents ListViewAction.tplの右端の六角スパナマークのボタンを押したときの制御

  • getFilterSelectElement  

  • registerChangeCustomFilterEvent

  • getFilterBlock
  • getListViewTopMenuContainer

オプションの上にポインタを乗せたとき

  • getSelectOptionFromChosenOption

フィルター名をクリックしたとき

  • getCurrentCvId
  • getAlphabetSearchValue
  • calculatePages
  • getPageCount
  • getDefaultParams
  • calculatePages
  • getPageCount
  • readSelectedIds
  • writeSelectedIds
  • updatePagination

f-Revoカスタマイズ -フィルターをいじるためのファイル構造

主なテンプレート

  • layouts\vlayout\modules\Vtiger\ListViewHeader.tpl
    ListViewのページの上のほうのヘッダー部分。 ユーザがフィルターを選択したり、顧客情報を追加するための機能をもつテンプレート

f:id:machikoikenouchi:20170417101022j:plain

  • layouts\vlayout\modules\CustomView\EditView.tpl
    EditViewページのテンプレート。AdvanceFilterをIncludeしている。

f:id:machikoikenouchi:20170417102928j:plain

  • layouts\vlayout\modules\Vtiger\AdvanceFilter.tpl

EditViewの子のテンプレート。
表示名:からのINPUT要素とチェックボタン三つがある。

  • layouts\vlayout\modules\Vtiger\AdvanceFilterCondition.tpl
    AdvanceFilterが親で、EditViewの孫テンプレート。
    すべての条件以下、フィルタ条件の設定を行う機能がある。

やりたいこと

f:id:machikoikenouchi:20170419110708j:plain

  1. EditView.tplの表示名:からのINPUTを値固定で無効化
  2. EditView.tplのチェックボタンをチェック状態で無効化×3
  3. AdvanceFilterCondition.tplの1つ目の条件のSELECTを値固定で無効化×2
  4. AdvanceFilterCondition.tplの1つ目の条件の識別のチェックボタンをチェック状態で無効化
  5. AdvanceFilterCondition.tplの1つ目の条件のINPUT-text要素を値固定で無効化
  6. AdvanceFilterCondition.tplの1つ目の条件の最後のゴミ箱ボタンを消す

実際にやってみる

1. EditView.tplの表示名:からのINPUTを値固定で無効化

{if ($CUSTOMVIEW_MODEL->get('viewname') eq '私のフィルター')}
        readonly
    {/if}

これをEditViewCondition.tpsの表示名:の後のINPUTタグの中に追加する だけでOKでした。

2. EditView.tplのチェックボタンをチェック状態で無効化×3

{if $CUSTOMVIEW_MODEL->get('viewname') eq '私のフィルター'}
    checked="checked" disabled
{else $CUSTOMVIEW_MODEL->isDefault()}
    checked="checked"
{/if}

これをEditViewCondition.tplの中のINPUT-checkcoxの中に追加する だけでOKでした。

3. AdvanceFilterCondition.tplの1つ目の条件のSELECTを値固定で無効化×2

f:id:machikoikenouchi:20170419113417p:plain

SELECTタグ2つの中に"disabled"という要素を追加します。

4. AdvanceFilterCondition.tplの1つ目の条件の識別のチェックボタンをチェック状態で無効化 f:id:machikoikenouchi:20170419114830p:plain

INPUT-checkboxタグ2つの中に"disabled"という要素を追加します。

5. AdvanceFilterCondition.tplの1つ目の条件のINPUT-text要素を値固定で無効化

f:id:machikoikenouchi:20170419115036p:plain

INPUT-checkboxタグ2つの中に"readonly"という要素を追加します。

6. AdvanceFilterCondition.tplの1つ目の条件の最後のゴミ箱ボタンを消す

<i class="deleteCondition icon-trash alignMiddle" title="{vtranslate('LBL_DELETE', $MODULE)}"></i>

これをただ消していきます。

※ここまでは正直簡単でした。
問題はここからです。

f:id:machikoikenouchi:20170419115223p:plain

これらを適応したあと、当たり前ですが要素が丸ごと コピーされちゃうので上の画像のようにボタンを 押すことでどんどん無効化のフォームが増えて行きます。

こうなったらJquery操作でなんとかするしかない

addNewCondition : function(conditionGroupElement){
    
var basicElement = jQuery('.basic',conditionGroupElement);
        //class="basic"を探して、ここをconditionGroupElementに設定
        //class="hide basic"を持つDIVはAdvanceFilter.tpl内にあります。
        //basicクラス内の要素をすべて消し、そこに追加していくという形です。

var newRowElement = basicElement.find('.conditionRow').clone(true,true);
        //AdvanceFilter.tplの中の<div class="conditionRow">をみつけてコピー(しかしここではコピーの出力はされない)

jQuery('select',newRowElement).addClass('chzn-select');
        //selectタグを探して、ここをnewRowElementとする。
        //そしてそこにclass='chzn-select'を生成する。
       
var conditionList = jQuery('.conditionList', conditionGroupElement);
        //class="conditionList"を探して、ここにもconditionGroupElementを設定。
conditionList.append(newRowElement);
//コピーしたものをconditionListクラスをもつタグに出力する。
        
 
 //ここに新たな処理 index=0のとき  一個クローンしたときにdisabledをはずす


 
app.changeSelectElementView(newRowElement);
        //app.js内にあるchangeSelectElementViewメソッドにnewRowElementを渡している。
       
return this;

    },

作りたいもの

私の担当フィルター

f:id:machikoikenouchi:20170414103749j:plain

アドレス:http://crm.snapshot.co.jp/index.php?module=Accounts&view=List

  • 新しいフィルターの作成を押す

  • ページ遷移http://crm.snapshot.co.jp/index.php?module=Accounts&view=List

f:id:machikoikenouchi:20170414183635j:plain

  • この画面で、登録する。

私の部署フィルター

これも上記同様にする。

f-Revo カスタマイズ①

新しいフィルターを登録したい

f:id:machikoikenouchi:20170414103749j:plain

このセレクトボタンの中身は下記です。

[layouts]-[vlayouts]-[Vtiger]-ListViewHeader.tpl

{foreach key=GROUP_LABEL item=GROUP_CUSTOM_VIEWS from=$CUSTOM_VIEWS}
<optgroup label=' {if $GROUP_LABEL eq 'Mine'} &nbsp; {else if} {vtranslate($GROUP_LABEL)} {/if}' >
    {foreach item="CUSTOM_VIEW" from=$GROUP_CUSTOM_VIEWS}[f:id:machikoikenouchi:20170414171455j:plain]
    <option  
        data-editurl="{$CUSTOM_VIEW->getEditUrl()}"
        data-deleteurl="{$CUSTOM_VIEW->getDeleteUrl()}"
        data-approveurl="{$CUSTOM_VIEW->getApproveUrl()}"
        data-denyurl="{$CUSTOM_VIEW->getDenyUrl()}"
        data-editable="{$CUSTOM_VIEW->isEditable()}"
        data-deletable="{$CUSTOM_VIEW->isDeletable()}"
        data-pending="{$CUSTOM_VIEW->isPending()}"
        data-public="{$CUSTOM_VIEW->isPublic() && $CURRENT_USER_MODEL->isAdminUser()}"
        id="filterOptionId_{$CUSTOM_VIEW->get('cvid')}" value="{$CUSTOM_VIEW->get('cvid')}"
        data-id="{$CUSTOM_VIEW->get('cvid')}"
        {if $VIEWID neq '' && $VIEWID neq '0'  && $VIEWID == $CUSTOM_VIEW->getId()}
        selected="selected"
        {elseif ($VIEWID == '' or $VIEWID == '0')&& $CUSTOM_VIEW->isDefault() eq 'true'}
        selected="selected" {/if}
        class="filterOptionId_{$CUSTOM_VIEW->get('cvid')}" >
     {*optionの中に仕込まれているもので中のフィルター名になっている気がする*}

     {if $CUSTOM_VIEW->get('viewname') eq 'All'}{vtranslate($CUSTOM_VIEW->get('viewname'), $MODULE)}
     {vtranslate($MODULE, $MODULE)}{else}{vtranslate($CUSTOM_VIEW->get('viewname'), $MODULE)}{/if}
     {if $GROUP_LABEL neq 'Mine'} [ {$CUSTOM_VIEW->getOwnerName()} ]  {/if}
    </option>
{/foreach}
</optgroup>
{/foreach}

ということで、Smarty(というHTMLテンプレートエンジン)が出てきます。
記法は以下に詳しく書いてあります。

http://www.smarty.net/

  • foreachとか、elseififはわかりました。
  • vtranslate()は見たことないです。公式にも乗ってないのでどこかで 定義されていそうです。

まずはこのやたら使われている$CUSTOM_VIEW

f:id:machikoikenouchi:20170414171617p:plain

  • Smarty特有のグローバル関数か?と思って検索してもすぐにはヒットしませんでしたので、たぶんこれもどこかで定義済みかと思って 調べました。CUSTOM_VIEWSじゃだめだったのでCUSTOMVIEWでいきました。
<?php$viewer->assign('CUSTOM_VIEWS', CustomView_Record_Model::getAllByGroup($moduleName));?>
  • assign(第一引数、第二引数) で括弧内のもの同士を結びつけ、代入することができます。 なのでこの場合、

  • Custom_View_Record_Model

広告を非表示にする

いまさら聞けないフレームワークの追い方ーphp

フレームワークの追い方

もう何からやってよいのか、皆目検討がつかない。。 そんなときの私なりの泥臭いやりかたです。

まずはデータベースから追ってみる方法

f:id:machikoikenouchi:20170414103749j:plain

やりたいこと:このページのフィルタ機能の フィルタ名のデータの流れを追って、改造したい。

f:id:machikoikenouchi:20170413164003j:plain

cvid viewname setdefault setmetrics entitiytype status useid
49 All 1 0 Accounts 0 1
68 顧客名 0 0 Accounts 1 1
70 顧客名称表示 0 0 Accounts 1 1
50 臨床 顧客一覧 0 0 Accounts 3 1
52 動物 顧客一覧 0 0 Accounts 3 1

1.A5M2でデータベースのテーブルを見ます。
今回はviewnameというカラム名を追って行きます。

2.今度はNetBeans起動。
CTRL+SHIFT+F でプロジェクト内検索をかけることができます。 f:id:machikoikenouchi:20170413164539j:plain

3.viewnameで検索をかけます。

f:id:machikoikenouchi:20170413164644j:plain

4.わーーーーっと検索結果が表示されました。
いくつかのphpファイルを見て、宣言されてるところを 探して以下来ます。

f:id:machikoikenouchi:20170413164848p:plain

5.ありました。 ListViewSession.phpです。

<?php
class ListViewSession {
    var $module = null;
    var $viewname = null;
    var $start = null;
    var $sorder = null;
    var $sortby = null;
    var $page_view = null;
//以下複数のfunctionがありましたが省略しています

?>
  • 全ソースを見たい人はこちら。
    http://~~

6.ここで$viewnameが初めて宣言されてきました。 さてこの関数はどのように使用されていくのでしょう。

<?php  // hasViewChanged()メソッド

function hasViewChanged($currentModule) {
  if(empty($_SESSION['lvs'][$currentModule]['viewname']))
    return true;
if(empty($_REQUEST['viewname'])) return false;

if($_REQUEST['viewname'] != $_SESSION['lvs'][$currentModule]['viewname']) 
  return true;
  return false;
    }
?>
<?php //setCurrentView()メソッド

    public static function setCurrentView($module, $viewId) {
        $_SESSION['lvs'][$module]['viewname'] = $viewId;
    }
?>
<?php //getCurrentView()メソッド
public static function getCurrentView($module) {
    if(!empty($_SESSION['lvs'][$module]['viewname'])) {
        return $_SESSION['lvs'][$module]['viewname'];
    }
        }
?>

7.この3つのメソッドが見つかりました。
1つ目以外はゲッターとセッターということがなんとなくわかると思います。

Viewから追って行く方法

f:id:machikoikenouchi:20170413171737j:plain

1.この原田フィルターを追って行きます。 まずはブラウザでお目当てのページ (localhost/プロジェクトフォルダ)を見てください。

f:id:machikoikenouchi:20170413171957j:plain

2.ブラウザでF12キーでデベロッパーツールを開きます。
カーソルを Select an element in the page to inspect itモードにして
対象に触れるとソース部分が選択されます。
※知らなかっf:id:machikoikenouchi:20170413174252j:plainたんですがこれSEPIモードっていうんですね・・

f:id:machikoikenouchi:20170413173129j:plain

3.この<span>~</span>が怪しそうです。
 当然、きれいなHTMLになっていますので、変数名などは見れません。
 なので、クラス名などから芋づる式に追って行きます。

<span>
<img class="filterImage" src="layouts/vlayout/skins/images/filter.png"
style="height: 13px; margin-right: 2px; vertical-align: middle;"
data-pin-nopin="true">原田テストフィルター
</span>

4.生成されたHTMLこのようになっています。 class="filterImage"とあります。

すごーい!このアイコンぽいのを使っているphpファイルを見つければいいんだね!! ・・・と当初は思っておりました。

5.さっきと同じようにNetBeansでCTRL+SHIFT+F class="filterImage"でプロジェクト検索かけます。

f:id:machikoikenouchi:20170413174252j:plain

<span class="customFilterMainSpan row-fluid">
 {if $CUSTOM_VIEWS|@count gt 0}
  <select id="recordsFilter" class="span12" data-placeholder="{vtranslate('LBL_SELECT_TO_LOAD_LIST', $RELATED_MODULE_NAME)}">
 <option></option>
 {foreach key=GROUP_LABEL item=GROUP_CUSTOM_VIEWS from=$CUSTOM_VIEWS}
 <optgroup label=' {if $GROUP_LABEL eq 'Mine'} &nbsp; {else if} {vtranslate($GROUP_LABEL)} {/if}' >
  
  {foreach item="CUSTOM_VIEW" from=$GROUP_CUSTOM_VIEWS}
  
   <option id="filterOptionId_{$CUSTOM_VIEW->get('cvid')}" value="{$CUSTOM_VIEW->get('cvid')}" class="filterOptionId_{$CUSTOM_VIEW->get('cvid')}" data-id="{$CUSTOM_VIEW->get('cvid')}">
  
  {if $CUSTOM_VIEW->get('viewname') eq 'All'}{vtranslate($CUSTOM_VIEW->get('viewname'), $RELATED_MODULE_NAME)}
  
  {vtranslate($RELATED_MODULE_NAME, $RELATED_MODULE_NAME)}{else}{vtranslate($CUSTOM_VIEW->get('viewname'), $RELATED_MODULE_NAME)}
  
  {/if}
  
  {if $GROUP_LABEL neq 'Mine'}
    [ {$CUSTOM_VIEW->getOwnerName()} ]
    
   {/if}
   
  </option>
 {/foreach}
 </optgroup>
{/foreach}
</select>
 <img class="filterImage" src="{'filter.png'|vimage_path}" style="display:none;height:13px;margin-right:2px;vertical-align: middle;">
     {else}
     <input type="hidden" value="0" id="customFilter" />
     {/if}
</span>

6.このように謎しか生みませんでした。しかしあきらめたらそこで試合終了ですよね! 3.で貼り付けたコードと見比べてみます。 そこでなんとなくどの位置なのかわかったら、とりあえず画面になんかだして確認しちゃいましょう。

<img class="filterImage" src="{'filter.png'|vimage_path}" style="display:none;height:13px;margin-right:2px;vertical-align: middle;">
//このあとがなんとなくそれっぽいので、入れてみます。

はらだでございます!

伝家の宝刀var_dump()を使おう

さてここまできました。なんとなく勘でやっても無理なので、 var_dump()使って検証していきますよ。

よく使うクラス※順次更新

ListViewController
  • ファイルの場所:[include]-[ListView]以下

  • 持っているフィールド

    • $queryGenerator (private)