メインコンテンツに移動

メインナビゲーション

  • ホーム
  • サイトマップ
  • ビデオ
  • ご連絡

パンくず

  • ホーム
  • Drupalのエンティティ(Entity)の権限管理設定は「access callback」を定義したほうが安全

Drupalのエンティティ(Entity)の権限管理設定は「access callback」を定義したほうが安全

drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
customization
development
menu

hook_menuで定義したページへのアクセス権限設定は「access callback」と「access arguments」で行います。特定なユーザーロール、特定なユーザーのみなどの制御が可能となります。特に、ユーザーのアクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)で各ページのアクセス権限管理が一般的で、よく利用されます。hook_menuがこのアクセス権限管理の実装方法を纏めます。

やりたいこと

  • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)へのユーザー権限管理実装
  • 管理者がユーザーのロールよりカスタマイズしたページへのアクセス管理ができます

アクセス権限:既存とカスタム2種類

  • 'access callback'と'access arguments'設定より特定なユーザーアクセスを制御します
    • 'access callback' => 'user_access'、 'access arguments' => array('access content') コンテンツの参照権限(すべてのユーザがこの権限を有するべき)
    • 'access arguments'の設定値はアクセス権限一覧を参考してください
  • hook_permissionを実装して、権限管理ページ(Home » Administration » People » Permissions)でアクセス権限を管理します
    • ​下記の例で詳細に実装方法を説明します

'access callback'と'access arguments'の違い

  • 'access callback': 関数名(例:'user_view_access')として設定、関数('user_view_access')が呼び出され、ユーザーがこのページにアクセス権限があるか否かを判断する機能となります
    • 初期値('access callback'が定義されていない場合): 'user_access'
    • 例:'access callback' => TRUE が設定された場合、すべてのユーザーがアクセス可能、この時'access arguments'の設定は不要
    • '例:'access callback' => 'user_is_logged_in' の場合、関数user_is_logged_inが呼び出され、True/Falseの判断結果を 'access callback'に返します
  • 'access arguments': 配列構造、特定の権限/ロールを'access callback'に設定された関数に、引数として渡す機能となります
    • ​'access callback' => 'user_access' , 'access arguments' => array('administer users') で設定した場合以下の権限設定となります
      • ​create entries
      • delete any entry
      • delete own entries
      • edit any entry
      • edit own entries
    •  配列に整数の設定(例:'access arguments' => array(1))ができます。この場合、URLのパラメータを参照して、​'access callback'に設定された関数に渡します
      • ​URL例: 'foo/bar/user'
      • array(0)は'foo'、array(1)は'bar'、array(2)は'user'を引数として'access callback'に設定された関数渡します

「access arguments」設定とhook_permissonでアクセス権限を制御

  • 「access arguments」関数に、hook_permissionを実装したファンクションを渡すこともできます
    • 例: 'access arguments'  => array('authorize fbauth_connect') 
    • hook_permissionを実装したファンクションは以下のようで(実に簡単の数行だけ)
      /**
       * Implements hook_permission().
       */
      function [my-module-name]_permission() {
        return array(
            'authorize fbauth_connect' => array(
                'title' => t('Connect Facebook account and store access token'),
            ),
        );
      }
      
    • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)に、このページへのアクセス権限管理が現れます
    • アクセス権限管理ページで、ユーザーのロールより定義したページへのアクセス管理ができます。
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
customization
development
menu

hook_menuで定義したページへのアクセス権限設定は「access callback」と「access arguments」で行います。特定なユーザーロール、特定なユーザーのみなどの制御が可能となります。特に、ユーザーのアクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)で各ページのアクセス権限管理が一般的で、よく利用されます。hook_menuがこのアクセス権限管理の実装方法を纏めます。

やりたいこと

  • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)へのユーザー権限管理実装
  • 管理者がユーザーのロールよりカスタマイズしたページへのアクセス管理ができます

アクセス権限:既存とカスタム2種類

  • 'access callback'と'access arguments'設定より特定なユーザーアクセスを制御します
    • 'access callback' => 'user_access'、 'access arguments' => array('access content') コンテンツの参照権限(すべてのユーザがこの権限を有するべき)
    • 'access arguments'の設定値はアクセス権限一覧を参考してください
  • hook_permissionを実装して、権限管理ページ(Home » Administration » People » Permissions)でアクセス権限を管理します
    • ​下記の例で詳細に実装方法を説明します

'access callback'と'access arguments'の違い

  • 'access callback': 関数名(例:'user_view_access')として設定、関数('user_view_access')が呼び出され、ユーザーがこのページにアクセス権限があるか否かを判断する機能となります
    • 初期値('access callback'が定義されていない場合): 'user_access'
    • 例:'access callback' => TRUE が設定された場合、すべてのユーザーがアクセス可能、この時'access arguments'の設定は不要
    • '例:'access callback' => 'user_is_logged_in' の場合、関数user_is_logged_inが呼び出され、True/Falseの判断結果を 'access callback'に返します
  • 'access arguments': 配列構造、特定の権限/ロールを'access callback'に設定された関数に、引数として渡す機能となります
    • ​'access callback' => 'user_access' , 'access arguments' => array('administer users') で設定した場合以下の権限設定となります
      • ​create entries
      • delete any entry
      • delete own entries
      • edit any entry
      • edit own entries
    •  配列に整数の設定(例:'access arguments' => array(1))ができます。この場合、URLのパラメータを参照して、​'access callback'に設定された関数に渡します
      • ​URL例: 'foo/bar/user'
      • array(0)は'foo'、array(1)は'bar'、array(2)は'user'を引数として'access callback'に設定された関数渡します

「access arguments」設定とhook_permissonでアクセス権限を制御

  • 「access arguments」関数に、hook_permissionを実装したファンクションを渡すこともできます
    • 例: 'access arguments'  => array('authorize fbauth_connect') 
    • hook_permissionを実装したファンクションは以下のようで(実に簡単の数行だけ)
      /**
       * Implements hook_permission().
       */
      function [my-module-name]_permission() {
        return array(
            'authorize fbauth_connect' => array(
                'title' => t('Connect Facebook account and store access token'),
            ),
        );
      }
      
    • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)に、このページへのアクセス権限管理が現れます
    • アクセス権限管理ページで、ユーザーのロールより定義したページへのアクセス管理ができます。
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
customization
development
menu

hook_menuで定義したページへのアクセス権限設定は「access callback」と「access arguments」で行います。特定なユーザーロール、特定なユーザーのみなどの制御が可能となります。特に、ユーザーのアクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)で各ページのアクセス権限管理が一般的で、よく利用されます。hook_menuがこのアクセス権限管理の実装方法を纏めます。

やりたいこと

  • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)へのユーザー権限管理実装
  • 管理者がユーザーのロールよりカスタマイズしたページへのアクセス管理ができます

アクセス権限:既存とカスタム2種類

  • 'access callback'と'access arguments'設定より特定なユーザーアクセスを制御します
    • 'access callback' => 'user_access'、 'access arguments' => array('access content') コンテンツの参照権限(すべてのユーザがこの権限を有するべき)
    • 'access arguments'の設定値はアクセス権限一覧を参考してください
  • hook_permissionを実装して、権限管理ページ(Home » Administration » People » Permissions)でアクセス権限を管理します
    • ​下記の例で詳細に実装方法を説明します

'access callback'と'access arguments'の違い

  • 'access callback': 関数名(例:'user_view_access')として設定、関数('user_view_access')が呼び出され、ユーザーがこのページにアクセス権限があるか否かを判断する機能となります
    • 初期値('access callback'が定義されていない場合): 'user_access'
    • 例:'access callback' => TRUE が設定された場合、すべてのユーザーがアクセス可能、この時'access arguments'の設定は不要
    • '例:'access callback' => 'user_is_logged_in' の場合、関数user_is_logged_inが呼び出され、True/Falseの判断結果を 'access callback'に返します
  • 'access arguments': 配列構造、特定の権限/ロールを'access callback'に設定された関数に、引数として渡す機能となります
    • ​'access callback' => 'user_access' , 'access arguments' => array('administer users') で設定した場合以下の権限設定となります
      • ​create entries
      • delete any entry
      • delete own entries
      • edit any entry
      • edit own entries
    •  配列に整数の設定(例:'access arguments' => array(1))ができます。この場合、URLのパラメータを参照して、​'access callback'に設定された関数に渡します
      • ​URL例: 'foo/bar/user'
      • array(0)は'foo'、array(1)は'bar'、array(2)は'user'を引数として'access callback'に設定された関数渡します

「access arguments」設定とhook_permissonでアクセス権限を制御

  • 「access arguments」関数に、hook_permissionを実装したファンクションを渡すこともできます
    • 例: 'access arguments'  => array('authorize fbauth_connect') 
    • hook_permissionを実装したファンクションは以下のようで(実に簡単の数行だけ)
      /**
       * Implements hook_permission().
       */
      function [my-module-name]_permission() {
        return array(
            'authorize fbauth_connect' => array(
                'title' => t('Connect Facebook account and store access token'),
            ),
        );
      }
      
    • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)に、このページへのアクセス権限管理が現れます
    • アクセス権限管理ページで、ユーザーのロールより定義したページへのアクセス管理ができます。
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
customization
development
menu

hook_menuで定義したページへのアクセス権限設定は「access callback」と「access arguments」で行います。特定なユーザーロール、特定なユーザーのみなどの制御が可能となります。特に、ユーザーのアクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)で各ページのアクセス権限管理が一般的で、よく利用されます。hook_menuがこのアクセス権限管理の実装方法を纏めます。

やりたいこと

  • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)へのユーザー権限管理実装
  • 管理者がユーザーのロールよりカスタマイズしたページへのアクセス管理ができます

アクセス権限:既存とカスタム2種類

  • 'access callback'と'access arguments'設定より特定なユーザーアクセスを制御します
    • 'access callback' => 'user_access'、 'access arguments' => array('access content') コンテンツの参照権限(すべてのユーザがこの権限を有するべき)
    • 'access arguments'の設定値はアクセス権限一覧を参考してください
  • hook_permissionを実装して、権限管理ページ(Home » Administration » People » Permissions)でアクセス権限を管理します
    • ​下記の例で詳細に実装方法を説明します

'access callback'と'access arguments'の違い

  • 'access callback': 関数名(例:'user_view_access')として設定、関数('user_view_access')が呼び出され、ユーザーがこのページにアクセス権限があるか否かを判断する機能となります
    • 初期値('access callback'が定義されていない場合): 'user_access'
    • 例:'access callback' => TRUE が設定された場合、すべてのユーザーがアクセス可能、この時'access arguments'の設定は不要
    • '例:'access callback' => 'user_is_logged_in' の場合、関数user_is_logged_inが呼び出され、True/Falseの判断結果を 'access callback'に返します
  • 'access arguments': 配列構造、特定の権限/ロールを'access callback'に設定された関数に、引数として渡す機能となります
    • ​'access callback' => 'user_access' , 'access arguments' => array('administer users') で設定した場合以下の権限設定となります
      • ​create entries
      • delete any entry
      • delete own entries
      • edit any entry
      • edit own entries
    •  配列に整数の設定(例:'access arguments' => array(1))ができます。この場合、URLのパラメータを参照して、​'access callback'に設定された関数に渡します
      • ​URL例: 'foo/bar/user'
      • array(0)は'foo'、array(1)は'bar'、array(2)は'user'を引数として'access callback'に設定された関数渡します

「access arguments」設定とhook_permissonでアクセス権限を制御

  • 「access arguments」関数に、hook_permissionを実装したファンクションを渡すこともできます
    • 例: 'access arguments'  => array('authorize fbauth_connect') 
    • hook_permissionを実装したファンクションは以下のようで(実に簡単の数行だけ)
      /**
       * Implements hook_permission().
       */
      function [my-module-name]_permission() {
        return array(
            'authorize fbauth_connect' => array(
                'title' => t('Connect Facebook account and store access token'),
            ),
        );
      }
      
    • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)に、このページへのアクセス権限管理が現れます
    • アクセス権限管理ページで、ユーザーのロールより定義したページへのアクセス管理ができます。
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
customization
development
menu

hook_menuで定義したページへのアクセス権限設定は「access callback」と「access arguments」で行います。特定なユーザーロール、特定なユーザーのみなどの制御が可能となります。特に、ユーザーのアクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)で各ページのアクセス権限管理が一般的で、よく利用されます。hook_menuがこのアクセス権限管理の実装方法を纏めます。

やりたいこと

  • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)へのユーザー権限管理実装
  • 管理者がユーザーのロールよりカスタマイズしたページへのアクセス管理ができます

アクセス権限:既存とカスタム2種類

  • 'access callback'と'access arguments'設定より特定なユーザーアクセスを制御します
    • 'access callback' => 'user_access'、 'access arguments' => array('access content') コンテンツの参照権限(すべてのユーザがこの権限を有するべき)
    • 'access arguments'の設定値はアクセス権限一覧を参考してください
  • hook_permissionを実装して、権限管理ページ(Home » Administration » People » Permissions)でアクセス権限を管理します
    • ​下記の例で詳細に実装方法を説明します

'access callback'と'access arguments'の違い

  • 'access callback': 関数名(例:'user_view_access')として設定、関数('user_view_access')が呼び出され、ユーザーがこのページにアクセス権限があるか否かを判断する機能となります
    • 初期値('access callback'が定義されていない場合): 'user_access'
    • 例:'access callback' => TRUE が設定された場合、すべてのユーザーがアクセス可能、この時'access arguments'の設定は不要
    • '例:'access callback' => 'user_is_logged_in' の場合、関数user_is_logged_inが呼び出され、True/Falseの判断結果を 'access callback'に返します
  • 'access arguments': 配列構造、特定の権限/ロールを'access callback'に設定された関数に、引数として渡す機能となります
    • ​'access callback' => 'user_access' , 'access arguments' => array('administer users') で設定した場合以下の権限設定となります
      • ​create entries
      • delete any entry
      • delete own entries
      • edit any entry
      • edit own entries
    •  配列に整数の設定(例:'access arguments' => array(1))ができます。この場合、URLのパラメータを参照して、​'access callback'に設定された関数に渡します
      • ​URL例: 'foo/bar/user'
      • array(0)は'foo'、array(1)は'bar'、array(2)は'user'を引数として'access callback'に設定された関数渡します

「access arguments」設定とhook_permissonでアクセス権限を制御

  • 「access arguments」関数に、hook_permissionを実装したファンクションを渡すこともできます
    • 例: 'access arguments'  => array('authorize fbauth_connect') 
    • hook_permissionを実装したファンクションは以下のようで(実に簡単の数行だけ)
      /**
       * Implements hook_permission().
       */
      function [my-module-name]_permission() {
        return array(
            'authorize fbauth_connect' => array(
                'title' => t('Connect Facebook account and store access token'),
            ),
        );
      }
      
    • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)に、このページへのアクセス権限管理が現れます
    • アクセス権限管理ページで、ユーザーのロールより定義したページへのアクセス管理ができます。
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
customization
development
menu

hook_menuで定義したページへのアクセス権限設定は「access callback」と「access arguments」で行います。特定なユーザーロール、特定なユーザーのみなどの制御が可能となります。特に、ユーザーのアクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)で各ページのアクセス権限管理が一般的で、よく利用されます。hook_menuがこのアクセス権限管理の実装方法を纏めます。

やりたいこと

  • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)へのユーザー権限管理実装
  • 管理者がユーザーのロールよりカスタマイズしたページへのアクセス管理ができます

アクセス権限:既存とカスタム2種類

  • 'access callback'と'access arguments'設定より特定なユーザーアクセスを制御します
    • 'access callback' => 'user_access'、 'access arguments' => array('access content') コンテンツの参照権限(すべてのユーザがこの権限を有するべき)
    • 'access arguments'の設定値はアクセス権限一覧を参考してください
  • hook_permissionを実装して、権限管理ページ(Home » Administration » People » Permissions)でアクセス権限を管理します
    • ​下記の例で詳細に実装方法を説明します

'access callback'と'access arguments'の違い

  • 'access callback': 関数名(例:'user_view_access')として設定、関数('user_view_access')が呼び出され、ユーザーがこのページにアクセス権限があるか否かを判断する機能となります
    • 初期値('access callback'が定義されていない場合): 'user_access'
    • 例:'access callback' => TRUE が設定された場合、すべてのユーザーがアクセス可能、この時'access arguments'の設定は不要
    • '例:'access callback' => 'user_is_logged_in' の場合、関数user_is_logged_inが呼び出され、True/Falseの判断結果を 'access callback'に返します
  • 'access arguments': 配列構造、特定の権限/ロールを'access callback'に設定された関数に、引数として渡す機能となります
    • ​'access callback' => 'user_access' , 'access arguments' => array('administer users') で設定した場合以下の権限設定となります
      • ​create entries
      • delete any entry
      • delete own entries
      • edit any entry
      • edit own entries
    •  配列に整数の設定(例:'access arguments' => array(1))ができます。この場合、URLのパラメータを参照して、​'access callback'に設定された関数に渡します
      • ​URL例: 'foo/bar/user'
      • array(0)は'foo'、array(1)は'bar'、array(2)は'user'を引数として'access callback'に設定された関数渡します

「access arguments」設定とhook_permissonでアクセス権限を制御

  • 「access arguments」関数に、hook_permissionを実装したファンクションを渡すこともできます
    • 例: 'access arguments'  => array('authorize fbauth_connect') 
    • hook_permissionを実装したファンクションは以下のようで(実に簡単の数行だけ)
      /**
       * Implements hook_permission().
       */
      function [my-module-name]_permission() {
        return array(
            'authorize fbauth_connect' => array(
                'title' => t('Connect Facebook account and store access token'),
            ),
        );
      }
      
    • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)に、このページへのアクセス権限管理が現れます
    • アクセス権限管理ページで、ユーザーのロールより定義したページへのアクセス管理ができます。
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
customization
development
menu

hook_menuで定義したページへのアクセス権限設定は「access callback」と「access arguments」で行います。特定なユーザーロール、特定なユーザーのみなどの制御が可能となります。特に、ユーザーのアクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)で各ページのアクセス権限管理が一般的で、よく利用されます。hook_menuがこのアクセス権限管理の実装方法を纏めます。

やりたいこと

  • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)へのユーザー権限管理実装
  • 管理者がユーザーのロールよりカスタマイズしたページへのアクセス管理ができます

アクセス権限:既存とカスタム2種類

  • 'access callback'と'access arguments'設定より特定なユーザーアクセスを制御します
    • 'access callback' => 'user_access'、 'access arguments' => array('access content') コンテンツの参照権限(すべてのユーザがこの権限を有するべき)
    • 'access arguments'の設定値はアクセス権限一覧を参考してください
  • hook_permissionを実装して、権限管理ページ(Home » Administration » People » Permissions)でアクセス権限を管理します
    • ​下記の例で詳細に実装方法を説明します

'access callback'と'access arguments'の違い

  • 'access callback': 関数名(例:'user_view_access')として設定、関数('user_view_access')が呼び出され、ユーザーがこのページにアクセス権限があるか否かを判断する機能となります
    • 初期値('access callback'が定義されていない場合): 'user_access'
    • 例:'access callback' => TRUE が設定された場合、すべてのユーザーがアクセス可能、この時'access arguments'の設定は不要
    • '例:'access callback' => 'user_is_logged_in' の場合、関数user_is_logged_inが呼び出され、True/Falseの判断結果を 'access callback'に返します
  • 'access arguments': 配列構造、特定の権限/ロールを'access callback'に設定された関数に、引数として渡す機能となります
    • ​'access callback' => 'user_access' , 'access arguments' => array('administer users') で設定した場合以下の権限設定となります
      • ​create entries
      • delete any entry
      • delete own entries
      • edit any entry
      • edit own entries
    •  配列に整数の設定(例:'access arguments' => array(1))ができます。この場合、URLのパラメータを参照して、​'access callback'に設定された関数に渡します
      • ​URL例: 'foo/bar/user'
      • array(0)は'foo'、array(1)は'bar'、array(2)は'user'を引数として'access callback'に設定された関数渡します

「access arguments」設定とhook_permissonでアクセス権限を制御

  • 「access arguments」関数に、hook_permissionを実装したファンクションを渡すこともできます
    • 例: 'access arguments'  => array('authorize fbauth_connect') 
    • hook_permissionを実装したファンクションは以下のようで(実に簡単の数行だけ)
      /**
       * Implements hook_permission().
       */
      function [my-module-name]_permission() {
        return array(
            'authorize fbauth_connect' => array(
                'title' => t('Connect Facebook account and store access token'),
            ),
        );
      }
      
    • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)に、このページへのアクセス権限管理が現れます
    • アクセス権限管理ページで、ユーザーのロールより定義したページへのアクセス管理ができます。
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
customization
development
menu

hook_menuで定義したページへのアクセス権限設定は「access callback」と「access arguments」で行います。特定なユーザーロール、特定なユーザーのみなどの制御が可能となります。特に、ユーザーのアクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)で各ページのアクセス権限管理が一般的で、よく利用されます。hook_menuがこのアクセス権限管理の実装方法を纏めます。

やりたいこと

  • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)へのユーザー権限管理実装
  • 管理者がユーザーのロールよりカスタマイズしたページへのアクセス管理ができます

アクセス権限:既存とカスタム2種類

  • 'access callback'と'access arguments'設定より特定なユーザーアクセスを制御します
    • 'access callback' => 'user_access'、 'access arguments' => array('access content') コンテンツの参照権限(すべてのユーザがこの権限を有するべき)
    • 'access arguments'の設定値はアクセス権限一覧を参考してください
  • hook_permissionを実装して、権限管理ページ(Home » Administration » People » Permissions)でアクセス権限を管理します
    • ​下記の例で詳細に実装方法を説明します

'access callback'と'access arguments'の違い

  • 'access callback': 関数名(例:'user_view_access')として設定、関数('user_view_access')が呼び出され、ユーザーがこのページにアクセス権限があるか否かを判断する機能となります
    • 初期値('access callback'が定義されていない場合): 'user_access'
    • 例:'access callback' => TRUE が設定された場合、すべてのユーザーがアクセス可能、この時'access arguments'の設定は不要
    • '例:'access callback' => 'user_is_logged_in' の場合、関数user_is_logged_inが呼び出され、True/Falseの判断結果を 'access callback'に返します
  • 'access arguments': 配列構造、特定の権限/ロールを'access callback'に設定された関数に、引数として渡す機能となります
    • ​'access callback' => 'user_access' , 'access arguments' => array('administer users') で設定した場合以下の権限設定となります
      • ​create entries
      • delete any entry
      • delete own entries
      • edit any entry
      • edit own entries
    •  配列に整数の設定(例:'access arguments' => array(1))ができます。この場合、URLのパラメータを参照して、​'access callback'に設定された関数に渡します
      • ​URL例: 'foo/bar/user'
      • array(0)は'foo'、array(1)は'bar'、array(2)は'user'を引数として'access callback'に設定された関数渡します

「access arguments」設定とhook_permissonでアクセス権限を制御

  • 「access arguments」関数に、hook_permissionを実装したファンクションを渡すこともできます
    • 例: 'access arguments'  => array('authorize fbauth_connect') 
    • hook_permissionを実装したファンクションは以下のようで(実に簡単の数行だけ)
      /**
       * Implements hook_permission().
       */
      function [my-module-name]_permission() {
        return array(
            'authorize fbauth_connect' => array(
                'title' => t('Connect Facebook account and store access token'),
            ),
        );
      }
      
    • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)に、このページへのアクセス権限管理が現れます
    • アクセス権限管理ページで、ユーザーのロールより定義したページへのアクセス管理ができます。
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
customization
development
menu

hook_menuで定義したページへのアクセス権限設定は「access callback」と「access arguments」で行います。特定なユーザーロール、特定なユーザーのみなどの制御が可能となります。特に、ユーザーのアクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)で各ページのアクセス権限管理が一般的で、よく利用されます。hook_menuがこのアクセス権限管理の実装方法を纏めます。

やりたいこと

  • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)へのユーザー権限管理実装
  • 管理者がユーザーのロールよりカスタマイズしたページへのアクセス管理ができます

アクセス権限:既存とカスタム2種類

  • 'access callback'と'access arguments'設定より特定なユーザーアクセスを制御します
    • 'access callback' => 'user_access'、 'access arguments' => array('access content') コンテンツの参照権限(すべてのユーザがこの権限を有するべき)
    • 'access arguments'の設定値はアクセス権限一覧を参考してください
  • hook_permissionを実装して、権限管理ページ(Home » Administration » People » Permissions)でアクセス権限を管理します
    • ​下記の例で詳細に実装方法を説明します

'access callback'と'access arguments'の違い

  • 'access callback': 関数名(例:'user_view_access')として設定、関数('user_view_access')が呼び出され、ユーザーがこのページにアクセス権限があるか否かを判断する機能となります
    • 初期値('access callback'が定義されていない場合): 'user_access'
    • 例:'access callback' => TRUE が設定された場合、すべてのユーザーがアクセス可能、この時'access arguments'の設定は不要
    • '例:'access callback' => 'user_is_logged_in' の場合、関数user_is_logged_inが呼び出され、True/Falseの判断結果を 'access callback'に返します
  • 'access arguments': 配列構造、特定の権限/ロールを'access callback'に設定された関数に、引数として渡す機能となります
    • ​'access callback' => 'user_access' , 'access arguments' => array('administer users') で設定した場合以下の権限設定となります
      • ​create entries
      • delete any entry
      • delete own entries
      • edit any entry
      • edit own entries
    •  配列に整数の設定(例:'access arguments' => array(1))ができます。この場合、URLのパラメータを参照して、​'access callback'に設定された関数に渡します
      • ​URL例: 'foo/bar/user'
      • array(0)は'foo'、array(1)は'bar'、array(2)は'user'を引数として'access callback'に設定された関数渡します

「access arguments」設定とhook_permissonでアクセス権限を制御

  • 「access arguments」関数に、hook_permissionを実装したファンクションを渡すこともできます
    • 例: 'access arguments'  => array('authorize fbauth_connect') 
    • hook_permissionを実装したファンクションは以下のようで(実に簡単の数行だけ)
      /**
       * Implements hook_permission().
       */
      function [my-module-name]_permission() {
        return array(
            'authorize fbauth_connect' => array(
                'title' => t('Connect Facebook account and store access token'),
            ),
        );
      }
      
    • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)に、このページへのアクセス権限管理が現れます
    • アクセス権限管理ページで、ユーザーのロールより定義したページへのアクセス管理ができます。
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
customization
development
menu

hook_menuで定義したページへのアクセス権限設定は「access callback」と「access arguments」で行います。特定なユーザーロール、特定なユーザーのみなどの制御が可能となります。特に、ユーザーのアクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)で各ページのアクセス権限管理が一般的で、よく利用されます。hook_menuがこのアクセス権限管理の実装方法を纏めます。

やりたいこと

  • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)へのユーザー権限管理実装
  • 管理者がユーザーのロールよりカスタマイズしたページへのアクセス管理ができます

アクセス権限:既存とカスタム2種類

  • 'access callback'と'access arguments'設定より特定なユーザーアクセスを制御します
    • 'access callback' => 'user_access'、 'access arguments' => array('access content') コンテンツの参照権限(すべてのユーザがこの権限を有するべき)
    • 'access arguments'の設定値はアクセス権限一覧を参考してください
  • hook_permissionを実装して、権限管理ページ(Home » Administration » People » Permissions)でアクセス権限を管理します
    • ​下記の例で詳細に実装方法を説明します

'access callback'と'access arguments'の違い

  • 'access callback': 関数名(例:'user_view_access')として設定、関数('user_view_access')が呼び出され、ユーザーがこのページにアクセス権限があるか否かを判断する機能となります
    • 初期値('access callback'が定義されていない場合): 'user_access'
    • 例:'access callback' => TRUE が設定された場合、すべてのユーザーがアクセス可能、この時'access arguments'の設定は不要
    • '例:'access callback' => 'user_is_logged_in' の場合、関数user_is_logged_inが呼び出され、True/Falseの判断結果を 'access callback'に返します
  • 'access arguments': 配列構造、特定の権限/ロールを'access callback'に設定された関数に、引数として渡す機能となります
    • ​'access callback' => 'user_access' , 'access arguments' => array('administer users') で設定した場合以下の権限設定となります
      • ​create entries
      • delete any entry
      • delete own entries
      • edit any entry
      • edit own entries
    •  配列に整数の設定(例:'access arguments' => array(1))ができます。この場合、URLのパラメータを参照して、​'access callback'に設定された関数に渡します
      • ​URL例: 'foo/bar/user'
      • array(0)は'foo'、array(1)は'bar'、array(2)は'user'を引数として'access callback'に設定された関数渡します

「access arguments」設定とhook_permissonでアクセス権限を制御

  • 「access arguments」関数に、hook_permissionを実装したファンクションを渡すこともできます
    • 例: 'access arguments'  => array('authorize fbauth_connect') 
    • hook_permissionを実装したファンクションは以下のようで(実に簡単の数行だけ)
      /**
       * Implements hook_permission().
       */
      function [my-module-name]_permission() {
        return array(
            'authorize fbauth_connect' => array(
                'title' => t('Connect Facebook account and store access token'),
            ),
        );
      }
      
    • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)に、このページへのアクセス権限管理が現れます
    • アクセス権限管理ページで、ユーザーのロールより定義したページへのアクセス管理ができます。
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
customization
development
menu

hook_menuで定義したページへのアクセス権限設定は「access callback」と「access arguments」で行います。特定なユーザーロール、特定なユーザーのみなどの制御が可能となります。特に、ユーザーのアクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)で各ページのアクセス権限管理が一般的で、よく利用されます。hook_menuがこのアクセス権限管理の実装方法を纏めます。

やりたいこと

  • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)へのユーザー権限管理実装
  • 管理者がユーザーのロールよりカスタマイズしたページへのアクセス管理ができます

アクセス権限:既存とカスタム2種類

  • 'access callback'と'access arguments'設定より特定なユーザーアクセスを制御します
    • 'access callback' => 'user_access'、 'access arguments' => array('access content') コンテンツの参照権限(すべてのユーザがこの権限を有するべき)
    • 'access arguments'の設定値はアクセス権限一覧を参考してください
  • hook_permissionを実装して、権限管理ページ(Home » Administration » People » Permissions)でアクセス権限を管理します
    • ​下記の例で詳細に実装方法を説明します

'access callback'と'access arguments'の違い

  • 'access callback': 関数名(例:'user_view_access')として設定、関数('user_view_access')が呼び出され、ユーザーがこのページにアクセス権限があるか否かを判断する機能となります
    • 初期値('access callback'が定義されていない場合): 'user_access'
    • 例:'access callback' => TRUE が設定された場合、すべてのユーザーがアクセス可能、この時'access arguments'の設定は不要
    • '例:'access callback' => 'user_is_logged_in' の場合、関数user_is_logged_inが呼び出され、True/Falseの判断結果を 'access callback'に返します
  • 'access arguments': 配列構造、特定の権限/ロールを'access callback'に設定された関数に、引数として渡す機能となります
    • ​'access callback' => 'user_access' , 'access arguments' => array('administer users') で設定した場合以下の権限設定となります
      • ​create entries
      • delete any entry
      • delete own entries
      • edit any entry
      • edit own entries
    •  配列に整数の設定(例:'access arguments' => array(1))ができます。この場合、URLのパラメータを参照して、​'access callback'に設定された関数に渡します
      • ​URL例: 'foo/bar/user'
      • array(0)は'foo'、array(1)は'bar'、array(2)は'user'を引数として'access callback'に設定された関数渡します

「access arguments」設定とhook_permissonでアクセス権限を制御

  • 「access arguments」関数に、hook_permissionを実装したファンクションを渡すこともできます
    • 例: 'access arguments'  => array('authorize fbauth_connect') 
    • hook_permissionを実装したファンクションは以下のようで(実に簡単の数行だけ)
      /**
       * Implements hook_permission().
       */
      function [my-module-name]_permission() {
        return array(
            'authorize fbauth_connect' => array(
                'title' => t('Connect Facebook account and store access token'),
            ),
        );
      }
      
    • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)に、このページへのアクセス権限管理が現れます
    • アクセス権限管理ページで、ユーザーのロールより定義したページへのアクセス管理ができます。
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
customization
development
menu

hook_menuで定義したページへのアクセス権限設定は「access callback」と「access arguments」で行います。特定なユーザーロール、特定なユーザーのみなどの制御が可能となります。特に、ユーザーのアクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)で各ページのアクセス権限管理が一般的で、よく利用されます。hook_menuがこのアクセス権限管理の実装方法を纏めます。

やりたいこと

  • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)へのユーザー権限管理実装
  • 管理者がユーザーのロールよりカスタマイズしたページへのアクセス管理ができます

アクセス権限:既存とカスタム2種類

  • 'access callback'と'access arguments'設定より特定なユーザーアクセスを制御します
    • 'access callback' => 'user_access'、 'access arguments' => array('access content') コンテンツの参照権限(すべてのユーザがこの権限を有するべき)
    • 'access arguments'の設定値はアクセス権限一覧を参考してください
  • hook_permissionを実装して、権限管理ページ(Home » Administration » People » Permissions)でアクセス権限を管理します
    • ​下記の例で詳細に実装方法を説明します

'access callback'と'access arguments'の違い

  • 'access callback': 関数名(例:'user_view_access')として設定、関数('user_view_access')が呼び出され、ユーザーがこのページにアクセス権限があるか否かを判断する機能となります
    • 初期値('access callback'が定義されていない場合): 'user_access'
    • 例:'access callback' => TRUE が設定された場合、すべてのユーザーがアクセス可能、この時'access arguments'の設定は不要
    • '例:'access callback' => 'user_is_logged_in' の場合、関数user_is_logged_inが呼び出され、True/Falseの判断結果を 'access callback'に返します
  • 'access arguments': 配列構造、特定の権限/ロールを'access callback'に設定された関数に、引数として渡す機能となります
    • ​'access callback' => 'user_access' , 'access arguments' => array('administer users') で設定した場合以下の権限設定となります
      • ​create entries
      • delete any entry
      • delete own entries
      • edit any entry
      • edit own entries
    •  配列に整数の設定(例:'access arguments' => array(1))ができます。この場合、URLのパラメータを参照して、​'access callback'に設定された関数に渡します
      • ​URL例: 'foo/bar/user'
      • array(0)は'foo'、array(1)は'bar'、array(2)は'user'を引数として'access callback'に設定された関数渡します

「access arguments」設定とhook_permissonでアクセス権限を制御

  • 「access arguments」関数に、hook_permissionを実装したファンクションを渡すこともできます
    • 例: 'access arguments'  => array('authorize fbauth_connect') 
    • hook_permissionを実装したファンクションは以下のようで(実に簡単の数行だけ)
      /**
       * Implements hook_permission().
       */
      function [my-module-name]_permission() {
        return array(
            'authorize fbauth_connect' => array(
                'title' => t('Connect Facebook account and store access token'),
            ),
        );
      }
      
    • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)に、このページへのアクセス権限管理が現れます
    • アクセス権限管理ページで、ユーザーのロールより定義したページへのアクセス管理ができます。
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
customization
development
menu

hook_menuで定義したページへのアクセス権限設定は「access callback」と「access arguments」で行います。特定なユーザーロール、特定なユーザーのみなどの制御が可能となります。特に、ユーザーのアクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)で各ページのアクセス権限管理が一般的で、よく利用されます。hook_menuがこのアクセス権限管理の実装方法を纏めます。

やりたいこと

  • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)へのユーザー権限管理実装
  • 管理者がユーザーのロールよりカスタマイズしたページへのアクセス管理ができます

アクセス権限:既存とカスタム2種類

  • 'access callback'と'access arguments'設定より特定なユーザーアクセスを制御します
    • 'access callback' => 'user_access'、 'access arguments' => array('access content') コンテンツの参照権限(すべてのユーザがこの権限を有するべき)
    • 'access arguments'の設定値はアクセス権限一覧を参考してください
  • hook_permissionを実装して、権限管理ページ(Home » Administration » People » Permissions)でアクセス権限を管理します
    • ​下記の例で詳細に実装方法を説明します

'access callback'と'access arguments'の違い

  • 'access callback': 関数名(例:'user_view_access')として設定、関数('user_view_access')が呼び出され、ユーザーがこのページにアクセス権限があるか否かを判断する機能となります
    • 初期値('access callback'が定義されていない場合): 'user_access'
    • 例:'access callback' => TRUE が設定された場合、すべてのユーザーがアクセス可能、この時'access arguments'の設定は不要
    • '例:'access callback' => 'user_is_logged_in' の場合、関数user_is_logged_inが呼び出され、True/Falseの判断結果を 'access callback'に返します
  • 'access arguments': 配列構造、特定の権限/ロールを'access callback'に設定された関数に、引数として渡す機能となります
    • ​'access callback' => 'user_access' , 'access arguments' => array('administer users') で設定した場合以下の権限設定となります
      • ​create entries
      • delete any entry
      • delete own entries
      • edit any entry
      • edit own entries
    •  配列に整数の設定(例:'access arguments' => array(1))ができます。この場合、URLのパラメータを参照して、​'access callback'に設定された関数に渡します
      • ​URL例: 'foo/bar/user'
      • array(0)は'foo'、array(1)は'bar'、array(2)は'user'を引数として'access callback'に設定された関数渡します

「access arguments」設定とhook_permissonでアクセス権限を制御

  • 「access arguments」関数に、hook_permissionを実装したファンクションを渡すこともできます
    • 例: 'access arguments'  => array('authorize fbauth_connect') 
    • hook_permissionを実装したファンクションは以下のようで(実に簡単の数行だけ)
      /**
       * Implements hook_permission().
       */
      function [my-module-name]_permission() {
        return array(
            'authorize fbauth_connect' => array(
                'title' => t('Connect Facebook account and store access token'),
            ),
        );
      }
      
    • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)に、このページへのアクセス権限管理が現れます
    • アクセス権限管理ページで、ユーザーのロールより定義したページへのアクセス管理ができます。
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
customization
development
menu

hook_menuで定義したページへのアクセス権限設定は「access callback」と「access arguments」で行います。特定なユーザーロール、特定なユーザーのみなどの制御が可能となります。特に、ユーザーのアクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)で各ページのアクセス権限管理が一般的で、よく利用されます。hook_menuがこのアクセス権限管理の実装方法を纏めます。

やりたいこと

  • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)へのユーザー権限管理実装
  • 管理者がユーザーのロールよりカスタマイズしたページへのアクセス管理ができます

アクセス権限:既存とカスタム2種類

  • 'access callback'と'access arguments'設定より特定なユーザーアクセスを制御します
    • 'access callback' => 'user_access'、 'access arguments' => array('access content') コンテンツの参照権限(すべてのユーザがこの権限を有するべき)
    • 'access arguments'の設定値はアクセス権限一覧を参考してください
  • hook_permissionを実装して、権限管理ページ(Home » Administration » People » Permissions)でアクセス権限を管理します
    • ​下記の例で詳細に実装方法を説明します

'access callback'と'access arguments'の違い

  • 'access callback': 関数名(例:'user_view_access')として設定、関数('user_view_access')が呼び出され、ユーザーがこのページにアクセス権限があるか否かを判断する機能となります
    • 初期値('access callback'が定義されていない場合): 'user_access'
    • 例:'access callback' => TRUE が設定された場合、すべてのユーザーがアクセス可能、この時'access arguments'の設定は不要
    • '例:'access callback' => 'user_is_logged_in' の場合、関数user_is_logged_inが呼び出され、True/Falseの判断結果を 'access callback'に返します
  • 'access arguments': 配列構造、特定の権限/ロールを'access callback'に設定された関数に、引数として渡す機能となります
    • ​'access callback' => 'user_access' , 'access arguments' => array('administer users') で設定した場合以下の権限設定となります
      • ​create entries
      • delete any entry
      • delete own entries
      • edit any entry
      • edit own entries
    •  配列に整数の設定(例:'access arguments' => array(1))ができます。この場合、URLのパラメータを参照して、​'access callback'に設定された関数に渡します
      • ​URL例: 'foo/bar/user'
      • array(0)は'foo'、array(1)は'bar'、array(2)は'user'を引数として'access callback'に設定された関数渡します

「access arguments」設定とhook_permissonでアクセス権限を制御

  • 「access arguments」関数に、hook_permissionを実装したファンクションを渡すこともできます
    • 例: 'access arguments'  => array('authorize fbauth_connect') 
    • hook_permissionを実装したファンクションは以下のようで(実に簡単の数行だけ)
      /**
       * Implements hook_permission().
       */
      function [my-module-name]_permission() {
        return array(
            'authorize fbauth_connect' => array(
                'title' => t('Connect Facebook account and store access token'),
            ),
        );
      }
      
    • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)に、このページへのアクセス権限管理が現れます
    • アクセス権限管理ページで、ユーザーのロールより定義したページへのアクセス管理ができます。
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
customization
development
menu

hook_menuで定義したページへのアクセス権限設定は「access callback」と「access arguments」で行います。特定なユーザーロール、特定なユーザーのみなどの制御が可能となります。特に、ユーザーのアクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)で各ページのアクセス権限管理が一般的で、よく利用されます。hook_menuがこのアクセス権限管理の実装方法を纏めます。

やりたいこと

  • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)へのユーザー権限管理実装
  • 管理者がユーザーのロールよりカスタマイズしたページへのアクセス管理ができます

アクセス権限:既存とカスタム2種類

  • 'access callback'と'access arguments'設定より特定なユーザーアクセスを制御します
    • 'access callback' => 'user_access'、 'access arguments' => array('access content') コンテンツの参照権限(すべてのユーザがこの権限を有するべき)
    • 'access arguments'の設定値はアクセス権限一覧を参考してください
  • hook_permissionを実装して、権限管理ページ(Home » Administration » People » Permissions)でアクセス権限を管理します
    • ​下記の例で詳細に実装方法を説明します

'access callback'と'access arguments'の違い

  • 'access callback': 関数名(例:'user_view_access')として設定、関数('user_view_access')が呼び出され、ユーザーがこのページにアクセス権限があるか否かを判断する機能となります
    • 初期値('access callback'が定義されていない場合): 'user_access'
    • 例:'access callback' => TRUE が設定された場合、すべてのユーザーがアクセス可能、この時'access arguments'の設定は不要
    • '例:'access callback' => 'user_is_logged_in' の場合、関数user_is_logged_inが呼び出され、True/Falseの判断結果を 'access callback'に返します
  • 'access arguments': 配列構造、特定の権限/ロールを'access callback'に設定された関数に、引数として渡す機能となります
    • ​'access callback' => 'user_access' , 'access arguments' => array('administer users') で設定した場合以下の権限設定となります
      • ​create entries
      • delete any entry
      • delete own entries
      • edit any entry
      • edit own entries
    •  配列に整数の設定(例:'access arguments' => array(1))ができます。この場合、URLのパラメータを参照して、​'access callback'に設定された関数に渡します
      • ​URL例: 'foo/bar/user'
      • array(0)は'foo'、array(1)は'bar'、array(2)は'user'を引数として'access callback'に設定された関数渡します

「access arguments」設定とhook_permissonでアクセス権限を制御

  • 「access arguments」関数に、hook_permissionを実装したファンクションを渡すこともできます
    • 例: 'access arguments'  => array('authorize fbauth_connect') 
    • hook_permissionを実装したファンクションは以下のようで(実に簡単の数行だけ)
      /**
       * Implements hook_permission().
       */
      function [my-module-name]_permission() {
        return array(
            'authorize fbauth_connect' => array(
                'title' => t('Connect Facebook account and store access token'),
            ),
        );
      }
      
    • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)に、このページへのアクセス権限管理が現れます
    • アクセス権限管理ページで、ユーザーのロールより定義したページへのアクセス管理ができます。
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
customization
development
menu

hook_menuで定義したページへのアクセス権限設定は「access callback」と「access arguments」で行います。特定なユーザーロール、特定なユーザーのみなどの制御が可能となります。特に、ユーザーのアクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)で各ページのアクセス権限管理が一般的で、よく利用されます。hook_menuがこのアクセス権限管理の実装方法を纏めます。

やりたいこと

  • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)へのユーザー権限管理実装
  • 管理者がユーザーのロールよりカスタマイズしたページへのアクセス管理ができます

アクセス権限:既存とカスタム2種類

  • 'access callback'と'access arguments'設定より特定なユーザーアクセスを制御します
    • 'access callback' => 'user_access'、 'access arguments' => array('access content') コンテンツの参照権限(すべてのユーザがこの権限を有するべき)
    • 'access arguments'の設定値はアクセス権限一覧を参考してください
  • hook_permissionを実装して、権限管理ページ(Home » Administration » People » Permissions)でアクセス権限を管理します
    • ​下記の例で詳細に実装方法を説明します

'access callback'と'access arguments'の違い

  • 'access callback': 関数名(例:'user_view_access')として設定、関数('user_view_access')が呼び出され、ユーザーがこのページにアクセス権限があるか否かを判断する機能となります
    • 初期値('access callback'が定義されていない場合): 'user_access'
    • 例:'access callback' => TRUE が設定された場合、すべてのユーザーがアクセス可能、この時'access arguments'の設定は不要
    • '例:'access callback' => 'user_is_logged_in' の場合、関数user_is_logged_inが呼び出され、True/Falseの判断結果を 'access callback'に返します
  • 'access arguments': 配列構造、特定の権限/ロールを'access callback'に設定された関数に、引数として渡す機能となります
    • ​'access callback' => 'user_access' , 'access arguments' => array('administer users') で設定した場合以下の権限設定となります
      • ​create entries
      • delete any entry
      • delete own entries
      • edit any entry
      • edit own entries
    •  配列に整数の設定(例:'access arguments' => array(1))ができます。この場合、URLのパラメータを参照して、​'access callback'に設定された関数に渡します
      • ​URL例: 'foo/bar/user'
      • array(0)は'foo'、array(1)は'bar'、array(2)は'user'を引数として'access callback'に設定された関数渡します

「access arguments」設定とhook_permissonでアクセス権限を制御

  • 「access arguments」関数に、hook_permissionを実装したファンクションを渡すこともできます
    • 例: 'access arguments'  => array('authorize fbauth_connect') 
    • hook_permissionを実装したファンクションは以下のようで(実に簡単の数行だけ)
      /**
       * Implements hook_permission().
       */
      function [my-module-name]_permission() {
        return array(
            'authorize fbauth_connect' => array(
                'title' => t('Connect Facebook account and store access token'),
            ),
        );
      }
      
    • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)に、このページへのアクセス権限管理が現れます
    • アクセス権限管理ページで、ユーザーのロールより定義したページへのアクセス管理ができます。
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
customization
development
menu

hook_menuで定義したページへのアクセス権限設定は「access callback」と「access arguments」で行います。特定なユーザーロール、特定なユーザーのみなどの制御が可能となります。特に、ユーザーのアクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)で各ページのアクセス権限管理が一般的で、よく利用されます。hook_menuがこのアクセス権限管理の実装方法を纏めます。

やりたいこと

  • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)へのユーザー権限管理実装
  • 管理者がユーザーのロールよりカスタマイズしたページへのアクセス管理ができます

アクセス権限:既存とカスタム2種類

  • 'access callback'と'access arguments'設定より特定なユーザーアクセスを制御します
    • 'access callback' => 'user_access'、 'access arguments' => array('access content') コンテンツの参照権限(すべてのユーザがこの権限を有するべき)
    • 'access arguments'の設定値はアクセス権限一覧を参考してください
  • hook_permissionを実装して、権限管理ページ(Home » Administration » People » Permissions)でアクセス権限を管理します
    • ​下記の例で詳細に実装方法を説明します

'access callback'と'access arguments'の違い

  • 'access callback': 関数名(例:'user_view_access')として設定、関数('user_view_access')が呼び出され、ユーザーがこのページにアクセス権限があるか否かを判断する機能となります
    • 初期値('access callback'が定義されていない場合): 'user_access'
    • 例:'access callback' => TRUE が設定された場合、すべてのユーザーがアクセス可能、この時'access arguments'の設定は不要
    • '例:'access callback' => 'user_is_logged_in' の場合、関数user_is_logged_inが呼び出され、True/Falseの判断結果を 'access callback'に返します
  • 'access arguments': 配列構造、特定の権限/ロールを'access callback'に設定された関数に、引数として渡す機能となります
    • ​'access callback' => 'user_access' , 'access arguments' => array('administer users') で設定した場合以下の権限設定となります
      • ​create entries
      • delete any entry
      • delete own entries
      • edit any entry
      • edit own entries
    •  配列に整数の設定(例:'access arguments' => array(1))ができます。この場合、URLのパラメータを参照して、​'access callback'に設定された関数に渡します
      • ​URL例: 'foo/bar/user'
      • array(0)は'foo'、array(1)は'bar'、array(2)は'user'を引数として'access callback'に設定された関数渡します

「access arguments」設定とhook_permissonでアクセス権限を制御

  • 「access arguments」関数に、hook_permissionを実装したファンクションを渡すこともできます
    • 例: 'access arguments'  => array('authorize fbauth_connect') 
    • hook_permissionを実装したファンクションは以下のようで(実に簡単の数行だけ)
      /**
       * Implements hook_permission().
       */
      function [my-module-name]_permission() {
        return array(
            'authorize fbauth_connect' => array(
                'title' => t('Connect Facebook account and store access token'),
            ),
        );
      }
      
    • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)に、このページへのアクセス権限管理が現れます
    • アクセス権限管理ページで、ユーザーのロールより定義したページへのアクセス管理ができます。
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
customization
development
menu

hook_menuで定義したページへのアクセス権限設定は「access callback」と「access arguments」で行います。特定なユーザーロール、特定なユーザーのみなどの制御が可能となります。特に、ユーザーのアクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)で各ページのアクセス権限管理が一般的で、よく利用されます。hook_menuがこのアクセス権限管理の実装方法を纏めます。

やりたいこと

  • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)へのユーザー権限管理実装
  • 管理者がユーザーのロールよりカスタマイズしたページへのアクセス管理ができます

アクセス権限:既存とカスタム2種類

  • 'access callback'と'access arguments'設定より特定なユーザーアクセスを制御します
    • 'access callback' => 'user_access'、 'access arguments' => array('access content') コンテンツの参照権限(すべてのユーザがこの権限を有するべき)
    • 'access arguments'の設定値はアクセス権限一覧を参考してください
  • hook_permissionを実装して、権限管理ページ(Home » Administration » People » Permissions)でアクセス権限を管理します
    • ​下記の例で詳細に実装方法を説明します

'access callback'と'access arguments'の違い

  • 'access callback': 関数名(例:'user_view_access')として設定、関数('user_view_access')が呼び出され、ユーザーがこのページにアクセス権限があるか否かを判断する機能となります
    • 初期値('access callback'が定義されていない場合): 'user_access'
    • 例:'access callback' => TRUE が設定された場合、すべてのユーザーがアクセス可能、この時'access arguments'の設定は不要
    • '例:'access callback' => 'user_is_logged_in' の場合、関数user_is_logged_inが呼び出され、True/Falseの判断結果を 'access callback'に返します
  • 'access arguments': 配列構造、特定の権限/ロールを'access callback'に設定された関数に、引数として渡す機能となります
    • ​'access callback' => 'user_access' , 'access arguments' => array('administer users') で設定した場合以下の権限設定となります
      • ​create entries
      • delete any entry
      • delete own entries
      • edit any entry
      • edit own entries
    •  配列に整数の設定(例:'access arguments' => array(1))ができます。この場合、URLのパラメータを参照して、​'access callback'に設定された関数に渡します
      • ​URL例: 'foo/bar/user'
      • array(0)は'foo'、array(1)は'bar'、array(2)は'user'を引数として'access callback'に設定された関数渡します

「access arguments」設定とhook_permissonでアクセス権限を制御

  • 「access arguments」関数に、hook_permissionを実装したファンクションを渡すこともできます
    • 例: 'access arguments'  => array('authorize fbauth_connect') 
    • hook_permissionを実装したファンクションは以下のようで(実に簡単の数行だけ)
      /**
       * Implements hook_permission().
       */
      function [my-module-name]_permission() {
        return array(
            'authorize fbauth_connect' => array(
                'title' => t('Connect Facebook account and store access token'),
            ),
        );
      }
      
    • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)に、このページへのアクセス権限管理が現れます
    • アクセス権限管理ページで、ユーザーのロールより定義したページへのアクセス管理ができます。
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
customization
development
menu

hook_menuで定義したページへのアクセス権限設定は「access callback」と「access arguments」で行います。特定なユーザーロール、特定なユーザーのみなどの制御が可能となります。特に、ユーザーのアクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)で各ページのアクセス権限管理が一般的で、よく利用されます。hook_menuがこのアクセス権限管理の実装方法を纏めます。

やりたいこと

  • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)へのユーザー権限管理実装
  • 管理者がユーザーのロールよりカスタマイズしたページへのアクセス管理ができます

アクセス権限:既存とカスタム2種類

  • 'access callback'と'access arguments'設定より特定なユーザーアクセスを制御します
    • 'access callback' => 'user_access'、 'access arguments' => array('access content') コンテンツの参照権限(すべてのユーザがこの権限を有するべき)
    • 'access arguments'の設定値はアクセス権限一覧を参考してください
  • hook_permissionを実装して、権限管理ページ(Home » Administration » People » Permissions)でアクセス権限を管理します
    • ​下記の例で詳細に実装方法を説明します

'access callback'と'access arguments'の違い

  • 'access callback': 関数名(例:'user_view_access')として設定、関数('user_view_access')が呼び出され、ユーザーがこのページにアクセス権限があるか否かを判断する機能となります
    • 初期値('access callback'が定義されていない場合): 'user_access'
    • 例:'access callback' => TRUE が設定された場合、すべてのユーザーがアクセス可能、この時'access arguments'の設定は不要
    • '例:'access callback' => 'user_is_logged_in' の場合、関数user_is_logged_inが呼び出され、True/Falseの判断結果を 'access callback'に返します
  • 'access arguments': 配列構造、特定の権限/ロールを'access callback'に設定された関数に、引数として渡す機能となります
    • ​'access callback' => 'user_access' , 'access arguments' => array('administer users') で設定した場合以下の権限設定となります
      • ​create entries
      • delete any entry
      • delete own entries
      • edit any entry
      • edit own entries
    •  配列に整数の設定(例:'access arguments' => array(1))ができます。この場合、URLのパラメータを参照して、​'access callback'に設定された関数に渡します
      • ​URL例: 'foo/bar/user'
      • array(0)は'foo'、array(1)は'bar'、array(2)は'user'を引数として'access callback'に設定された関数渡します

「access arguments」設定とhook_permissonでアクセス権限を制御

  • 「access arguments」関数に、hook_permissionを実装したファンクションを渡すこともできます
    • 例: 'access arguments'  => array('authorize fbauth_connect') 
    • hook_permissionを実装したファンクションは以下のようで(実に簡単の数行だけ)
      /**
       * Implements hook_permission().
       */
      function [my-module-name]_permission() {
        return array(
            'authorize fbauth_connect' => array(
                'title' => t('Connect Facebook account and store access token'),
            ),
        );
      }
      
    • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)に、このページへのアクセス権限管理が現れます
    • アクセス権限管理ページで、ユーザーのロールより定義したページへのアクセス管理ができます。
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
customization
development
menu

hook_menuで定義したページへのアクセス権限設定は「access callback」と「access arguments」で行います。特定なユーザーロール、特定なユーザーのみなどの制御が可能となります。特に、ユーザーのアクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)で各ページのアクセス権限管理が一般的で、よく利用されます。hook_menuがこのアクセス権限管理の実装方法を纏めます。

やりたいこと

  • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)へのユーザー権限管理実装
  • 管理者がユーザーのロールよりカスタマイズしたページへのアクセス管理ができます

アクセス権限:既存とカスタム2種類

  • 'access callback'と'access arguments'設定より特定なユーザーアクセスを制御します
    • 'access callback' => 'user_access'、 'access arguments' => array('access content') コンテンツの参照権限(すべてのユーザがこの権限を有するべき)
    • 'access arguments'の設定値はアクセス権限一覧を参考してください
  • hook_permissionを実装して、権限管理ページ(Home » Administration » People » Permissions)でアクセス権限を管理します
    • ​下記の例で詳細に実装方法を説明します

'access callback'と'access arguments'の違い

  • 'access callback': 関数名(例:'user_view_access')として設定、関数('user_view_access')が呼び出され、ユーザーがこのページにアクセス権限があるか否かを判断する機能となります
    • 初期値('access callback'が定義されていない場合): 'user_access'
    • 例:'access callback' => TRUE が設定された場合、すべてのユーザーがアクセス可能、この時'access arguments'の設定は不要
    • '例:'access callback' => 'user_is_logged_in' の場合、関数user_is_logged_inが呼び出され、True/Falseの判断結果を 'access callback'に返します
  • 'access arguments': 配列構造、特定の権限/ロールを'access callback'に設定された関数に、引数として渡す機能となります
    • ​'access callback' => 'user_access' , 'access arguments' => array('administer users') で設定した場合以下の権限設定となります
      • ​create entries
      • delete any entry
      • delete own entries
      • edit any entry
      • edit own entries
    •  配列に整数の設定(例:'access arguments' => array(1))ができます。この場合、URLのパラメータを参照して、​'access callback'に設定された関数に渡します
      • ​URL例: 'foo/bar/user'
      • array(0)は'foo'、array(1)は'bar'、array(2)は'user'を引数として'access callback'に設定された関数渡します

「access arguments」設定とhook_permissonでアクセス権限を制御

  • 「access arguments」関数に、hook_permissionを実装したファンクションを渡すこともできます
    • 例: 'access arguments'  => array('authorize fbauth_connect') 
    • hook_permissionを実装したファンクションは以下のようで(実に簡単の数行だけ)
      /**
       * Implements hook_permission().
       */
      function [my-module-name]_permission() {
        return array(
            'authorize fbauth_connect' => array(
                'title' => t('Connect Facebook account and store access token'),
            ),
        );
      }
      
    • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)に、このページへのアクセス権限管理が現れます
    • アクセス権限管理ページで、ユーザーのロールより定義したページへのアクセス管理ができます。
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
customization
development
menu

hook_menuで定義したページへのアクセス権限設定は「access callback」と「access arguments」で行います。特定なユーザーロール、特定なユーザーのみなどの制御が可能となります。特に、ユーザーのアクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)で各ページのアクセス権限管理が一般的で、よく利用されます。hook_menuがこのアクセス権限管理の実装方法を纏めます。

やりたいこと

  • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)へのユーザー権限管理実装
  • 管理者がユーザーのロールよりカスタマイズしたページへのアクセス管理ができます

アクセス権限:既存とカスタム2種類

  • 'access callback'と'access arguments'設定より特定なユーザーアクセスを制御します
    • 'access callback' => 'user_access'、 'access arguments' => array('access content') コンテンツの参照権限(すべてのユーザがこの権限を有するべき)
    • 'access arguments'の設定値はアクセス権限一覧を参考してください
  • hook_permissionを実装して、権限管理ページ(Home » Administration » People » Permissions)でアクセス権限を管理します
    • ​下記の例で詳細に実装方法を説明します

'access callback'と'access arguments'の違い

  • 'access callback': 関数名(例:'user_view_access')として設定、関数('user_view_access')が呼び出され、ユーザーがこのページにアクセス権限があるか否かを判断する機能となります
    • 初期値('access callback'が定義されていない場合): 'user_access'
    • 例:'access callback' => TRUE が設定された場合、すべてのユーザーがアクセス可能、この時'access arguments'の設定は不要
    • '例:'access callback' => 'user_is_logged_in' の場合、関数user_is_logged_inが呼び出され、True/Falseの判断結果を 'access callback'に返します
  • 'access arguments': 配列構造、特定の権限/ロールを'access callback'に設定された関数に、引数として渡す機能となります
    • ​'access callback' => 'user_access' , 'access arguments' => array('administer users') で設定した場合以下の権限設定となります
      • ​create entries
      • delete any entry
      • delete own entries
      • edit any entry
      • edit own entries
    •  配列に整数の設定(例:'access arguments' => array(1))ができます。この場合、URLのパラメータを参照して、​'access callback'に設定された関数に渡します
      • ​URL例: 'foo/bar/user'
      • array(0)は'foo'、array(1)は'bar'、array(2)は'user'を引数として'access callback'に設定された関数渡します

「access arguments」設定とhook_permissonでアクセス権限を制御

  • 「access arguments」関数に、hook_permissionを実装したファンクションを渡すこともできます
    • 例: 'access arguments'  => array('authorize fbauth_connect') 
    • hook_permissionを実装したファンクションは以下のようで(実に簡単の数行だけ)
      /**
       * Implements hook_permission().
       */
      function [my-module-name]_permission() {
        return array(
            'authorize fbauth_connect' => array(
                'title' => t('Connect Facebook account and store access token'),
            ),
        );
      }
      
    • ​アクセス権限管理ページ(ホーム » 管理 » ユーザー » 権限)に、このページへのアクセス権限管理が現れます
    • アクセス権限管理ページで、ユーザーのロールより定義したページへのアクセス管理ができます。
drupal
entity type
rules

Drupalのモジュール(EntityAPI)がモジュール(Rules、Viewsなど)に統合機能を提供していますが、各モジュール間の統一性が欠けています

  • 背景:Drupal7.56、EntityAPI7.x-1.8、Rules7.x-2.10
  • モジュール(EntityAPI)がほかのメジャーモジュール(Rules、Viewsなど)に統合機能を提供して、EntityAPIがよく利用されるようになります
  • ただし、各モジュール間の連携に統一性が欠けていることを気が付きました
    • 開発メンバーが違うので、開発ドキュメントが不備の場合、いろいろな誤解が生じるでしょう

例:エンティティ(Entity)へのアクセス権限管理にモジュール(Rules)が独自の権限("save")を生み出している

  • エンティティのアクセス権限チェック関数:entity_access($op, $entity_type, $entity = NULL, $account = NULL) (本家のentity_accessについての説明をご参考)
    • 引数$opは四つの権限:'view', 'update', 'create' or 'delete' (hook_entity_infoのパラメーター"access callback"を実装時にこの四つの権限を定義すればよいはずです)
  • モジュール(Rules)がエンティティアクセスチェック時に、"save"権限を生み出しています
    • rules/modules/entity_rules.incの313行あたモジュール(Rules)がエンティティに”save”権限を追加
    • 元々、モジュール(Rules)の"entity_save"は、「エンティティデータの保存」アクションです
    • ここで、"entity_save"を"save"に変換して、エンティティの"access callback"にパラメーターとして渡し、権限チェックをします
    • エンティティが定義している"access callback"に"save"権限を定義してなければ、権限がないと判断されます

対応方法1:モジュール(Rules)側でエンティティへのアクセス権限を再定義する

  • この例を限っては、モジュール(Rules)側で、エンティティへのアクセス権限の再定義します
    • Rulesの設定で「権限チェック省略」オプションがあるので、権限チェックを行わないことができます
    • Rulesの権限管理で各タイプのユーザーに権限を付与することもできます
      モジュール(Rules)でエンティティへのアクセス権限をユーザーに付与する設定

対応方法2:エンティティ側の"access callback"に"save"権限チェックを追加

  • hook_entity_info()の"access callback"の実装に、"save"権限を追加します("update"権限と同じはず)
    /**
     * Access callback for endity
     */
    function vocabulary_access($op, $entity, $account = NULL, $entity_type = NULL) {
        global $user;
    
        if (!isset($account)) {
            $account = $user;
        }
        switch ($op) {
            case 'create':
                return user_access('administer vocabulary', $account)
                || user_access('create vocabulary', $account);
            case 'view':
                return user_access('administer vocabulary', $account)
                || user_access('view vocabulary', $account);
            case 'edit':
            case 'save':    // save permission created by  entity_rule.inc
                return user_access('administer vocabulary')
                || user_access('edit any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
            case 'delete':
                return user_access('administer vocabulary')
                || user_access('delete any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
        }
    }
  • サードパーティーのモジュールでは、ソースコードの変更はしないほうがよいでしょう。対応方法1をお勧めします
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
form
menu

Drupalのhook_menu()がカスタムページ作成時によく使用されます

  • Drupalのサイトにページを追加する手段はいろいろあります
  • モジュールでページを追加し、メニューをGUIで設定することができます
    • モジュール(Views)で検索結果をページとして表示することができます(主に表示系のページ、入力フォームはなし)
    • モジュール(Panels)で各種要素を集めてページを作成します(主に表示系のページ、入力フォームなし)
    • モジュール(Web Form)でコンタクト/アンケートなどのフォームページを作成(フォーム系、入力して、データをサーバーに送信して保存)
  • プログラミングでページを作成する場合にhook_menu()を実装する必要があります
    • hook_menu()を実装時に、いろいろなパラメーター設定が紛らわしくて、注意する必要があります
    • Drupalのhook_menuでのURL設定にワイドカードの利用
    • Drupalのhook_menuでのユーザーロール別アクセス権限管理
    • 今回は、「page callback」パラメーターを詳細を説明します

Drupalのhook_menu()のパラメーター「page callback」が直接にページの呼び出しと「drupal_get_form」を介してページを呼び出す

  • ここは、ajaxで動作するチェックボックスを例として上げます
    • チェックボックスをクリックすると、ajaxでサーバーと交信する
      function ajax_add_to_my_vocabulary($form,&$form_state, $entity){
      
          global $user ;
      
          //  データがすでに存在するか否かのチェック
          $my_voca_result = db_query("SELECT entity_id FROM my_ch_vocabulary WHERE vid=".$entity->vid . " AND uid=".$user->uid)->fetchAll();
          //   ajaxコールバック時に使用するパラメーター
          $form_state['has_my_voca_result'] = count($my_voca_result)>0 ? true:false ;
          $form_state['ch_vocabulary'] = $entity ;  
      
          // ajaxのチェックボックスフォーム定義
          $form['ch_voca_form']=array(
              '#type' => 'checkbox',
              '#disabled' => $user->uid == 0 ? true : false ,
              '#title' => count($my_voca_result)>0 ? t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>') : t('<span id="ch-voca-check-op-'.$entity->vid.'">Add</span>'),
              '#default_value' => count($my_voca_result)>0 ? 1 : 0,
              '#ajax' => array(
                  'callback' => 'add_to_my_vocabulary_callback',
                  'wrapper' => 'ch-voca-check-op-'.$entity->vid,
                  'method' => 'replace',
                  'effect' => 'fade',
              ),
          );
          return $form ;
      }
      
      // ajaxのコールバック関数
      function add_to_my_vocabulary_in_views($form, $form_state){
      
          global $user;
      
          $entity = $form_state['ch_vocabulary'] ;
      
          // DBにデータがある場合に削除処理
          if( $form_state['has_my_voca_result'] ) {
              db_delete( 'my_ch_vocabulary' )->condition( 'vid', $entity->vid )->condition('uid', $user->uid )->execute();
              //  チェックボックスのラベルを「Add」に変更
              return t('<span id="ch-voca-check-op-'.$vid.'">Add</span>');
          }
      
          // データがなければ、ch_vocabuaryをmy_vocabuaryにコピー
          $my_voca = entity_create('my_ch_vocabulary', array('vid'=>$entity->vid));
          $my_voca_wrapper = entity_metadata_wrapper( 'my_ch_vocabulary', $my_voca );
          $my_voca_wrapper->vocabulary->set( $ch_voca->vocabulary ) ;
          ....   //各種フィールド、プロパティのセット
          $my_voca_wrapper->save();   // コピーされたデータの保存
      
          //  チェックボックスのラベルを「Remove」に変更
          return  t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>');
      }
  • hook_menu()が直接にajaxのチェックボックスフォームを呼び出す場合、ajaxの動作しません
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
            'title' => 'Copy and Remove',
            'page callback' => 'ajax_add_to_my_vocabulary',
            'page arguments' => array(array(), array(), 2) ,
            'access arguments' => array('create my vocabulary'),
      );
    }
    • ajaxのチェックボックスフォーム「ajax_add_to_my_vocabulary」が表示されますが、<form>要素がなくて、ajaxの動作はしません
      hook_menuが直接のフォームを呼び出す場合<form>要素が作成されずチェックボックスの動作はしない
  • hook_menu()が"drupal_get_form"を介してにajaxのチェックボックスフォームを呼び出す場合、ajaxの動作は正常です
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
       'title' => 'Copy and Remove', 
       'page callback' => 'drupal_get_form', 
       'page arguments' => array('ajax_add_to_my_vocabulary', 2) , 
       'access arguments' => array('create my vocabulary'), 
      ); 
    }
    • 作成されたフォームに"<form>"要素およびajaxの要素が含まれているのでajaxが正常に動作します
      hook_menuが"drupal_get_form"を介してフォームを呼び出す場合に"<form>"要素が含まれてajaxが正常に動作

結論:表示系ページは直接に呼び出し、入力フォーム系は"drupal_get_form"を介して呼び出す

  • hook_menu()を実装時に、呼び出すページの性質によりページの呼び出す方法を決めます
    • 表示系ページ(データ更新なし)の場合は直接に呼び出せばよいです
    • 入力フォーム系のページは"drupal_get_form"を介して呼び出します
  • 表示系か、フォーム系かを区別せず、いつも"drupal_get_form"を介して呼び出しても構わないです
  • "drupal_get_form"がフォーム構築のプロセスが含まているので、フォームの要素構築するには必要です
    • drupa_get_form, drupal_prepare_form, drupal_retrieve_formの違い
    • これを使用する場合少し(どのぐらいは測定していないが)遅いかもしれないです
drupal
entity type
rules

Drupalのモジュール(EntityAPI)がモジュール(Rules、Viewsなど)に統合機能を提供していますが、各モジュール間の統一性が欠けています

  • 背景:Drupal7.56、EntityAPI7.x-1.8、Rules7.x-2.10
  • モジュール(EntityAPI)がほかのメジャーモジュール(Rules、Viewsなど)に統合機能を提供して、EntityAPIがよく利用されるようになります
  • ただし、各モジュール間の連携に統一性が欠けていることを気が付きました
    • 開発メンバーが違うので、開発ドキュメントが不備の場合、いろいろな誤解が生じるでしょう

例:エンティティ(Entity)へのアクセス権限管理にモジュール(Rules)が独自の権限("save")を生み出している

  • エンティティのアクセス権限チェック関数:entity_access($op, $entity_type, $entity = NULL, $account = NULL) (本家のentity_accessについての説明をご参考)
    • 引数$opは四つの権限:'view', 'update', 'create' or 'delete' (hook_entity_infoのパラメーター"access callback"を実装時にこの四つの権限を定義すればよいはずです)
  • モジュール(Rules)がエンティティアクセスチェック時に、"save"権限を生み出しています
    • rules/modules/entity_rules.incの313行あたモジュール(Rules)がエンティティに”save”権限を追加
    • 元々、モジュール(Rules)の"entity_save"は、「エンティティデータの保存」アクションです
    • ここで、"entity_save"を"save"に変換して、エンティティの"access callback"にパラメーターとして渡し、権限チェックをします
    • エンティティが定義している"access callback"に"save"権限を定義してなければ、権限がないと判断されます

対応方法1:モジュール(Rules)側でエンティティへのアクセス権限を再定義する

  • この例を限っては、モジュール(Rules)側で、エンティティへのアクセス権限の再定義します
    • Rulesの設定で「権限チェック省略」オプションがあるので、権限チェックを行わないことができます
    • Rulesの権限管理で各タイプのユーザーに権限を付与することもできます
      モジュール(Rules)でエンティティへのアクセス権限をユーザーに付与する設定

対応方法2:エンティティ側の"access callback"に"save"権限チェックを追加

  • hook_entity_info()の"access callback"の実装に、"save"権限を追加します("update"権限と同じはず)
    /**
     * Access callback for endity
     */
    function vocabulary_access($op, $entity, $account = NULL, $entity_type = NULL) {
        global $user;
    
        if (!isset($account)) {
            $account = $user;
        }
        switch ($op) {
            case 'create':
                return user_access('administer vocabulary', $account)
                || user_access('create vocabulary', $account);
            case 'view':
                return user_access('administer vocabulary', $account)
                || user_access('view vocabulary', $account);
            case 'edit':
            case 'save':    // save permission created by  entity_rule.inc
                return user_access('administer vocabulary')
                || user_access('edit any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
            case 'delete':
                return user_access('administer vocabulary')
                || user_access('delete any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
        }
    }
  • サードパーティーのモジュールでは、ソースコードの変更はしないほうがよいでしょう。対応方法1をお勧めします
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
form
menu

Drupalのhook_menu()がカスタムページ作成時によく使用されます

  • Drupalのサイトにページを追加する手段はいろいろあります
  • モジュールでページを追加し、メニューをGUIで設定することができます
    • モジュール(Views)で検索結果をページとして表示することができます(主に表示系のページ、入力フォームはなし)
    • モジュール(Panels)で各種要素を集めてページを作成します(主に表示系のページ、入力フォームなし)
    • モジュール(Web Form)でコンタクト/アンケートなどのフォームページを作成(フォーム系、入力して、データをサーバーに送信して保存)
  • プログラミングでページを作成する場合にhook_menu()を実装する必要があります
    • hook_menu()を実装時に、いろいろなパラメーター設定が紛らわしくて、注意する必要があります
    • Drupalのhook_menuでのURL設定にワイドカードの利用
    • Drupalのhook_menuでのユーザーロール別アクセス権限管理
    • 今回は、「page callback」パラメーターを詳細を説明します

Drupalのhook_menu()のパラメーター「page callback」が直接にページの呼び出しと「drupal_get_form」を介してページを呼び出す

  • ここは、ajaxで動作するチェックボックスを例として上げます
    • チェックボックスをクリックすると、ajaxでサーバーと交信する
      function ajax_add_to_my_vocabulary($form,&$form_state, $entity){
      
          global $user ;
      
          //  データがすでに存在するか否かのチェック
          $my_voca_result = db_query("SELECT entity_id FROM my_ch_vocabulary WHERE vid=".$entity->vid . " AND uid=".$user->uid)->fetchAll();
          //   ajaxコールバック時に使用するパラメーター
          $form_state['has_my_voca_result'] = count($my_voca_result)>0 ? true:false ;
          $form_state['ch_vocabulary'] = $entity ;  
      
          // ajaxのチェックボックスフォーム定義
          $form['ch_voca_form']=array(
              '#type' => 'checkbox',
              '#disabled' => $user->uid == 0 ? true : false ,
              '#title' => count($my_voca_result)>0 ? t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>') : t('<span id="ch-voca-check-op-'.$entity->vid.'">Add</span>'),
              '#default_value' => count($my_voca_result)>0 ? 1 : 0,
              '#ajax' => array(
                  'callback' => 'add_to_my_vocabulary_callback',
                  'wrapper' => 'ch-voca-check-op-'.$entity->vid,
                  'method' => 'replace',
                  'effect' => 'fade',
              ),
          );
          return $form ;
      }
      
      // ajaxのコールバック関数
      function add_to_my_vocabulary_in_views($form, $form_state){
      
          global $user;
      
          $entity = $form_state['ch_vocabulary'] ;
      
          // DBにデータがある場合に削除処理
          if( $form_state['has_my_voca_result'] ) {
              db_delete( 'my_ch_vocabulary' )->condition( 'vid', $entity->vid )->condition('uid', $user->uid )->execute();
              //  チェックボックスのラベルを「Add」に変更
              return t('<span id="ch-voca-check-op-'.$vid.'">Add</span>');
          }
      
          // データがなければ、ch_vocabuaryをmy_vocabuaryにコピー
          $my_voca = entity_create('my_ch_vocabulary', array('vid'=>$entity->vid));
          $my_voca_wrapper = entity_metadata_wrapper( 'my_ch_vocabulary', $my_voca );
          $my_voca_wrapper->vocabulary->set( $ch_voca->vocabulary ) ;
          ....   //各種フィールド、プロパティのセット
          $my_voca_wrapper->save();   // コピーされたデータの保存
      
          //  チェックボックスのラベルを「Remove」に変更
          return  t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>');
      }
  • hook_menu()が直接にajaxのチェックボックスフォームを呼び出す場合、ajaxの動作しません
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
            'title' => 'Copy and Remove',
            'page callback' => 'ajax_add_to_my_vocabulary',
            'page arguments' => array(array(), array(), 2) ,
            'access arguments' => array('create my vocabulary'),
      );
    }
    • ajaxのチェックボックスフォーム「ajax_add_to_my_vocabulary」が表示されますが、<form>要素がなくて、ajaxの動作はしません
      hook_menuが直接のフォームを呼び出す場合<form>要素が作成されずチェックボックスの動作はしない
  • hook_menu()が"drupal_get_form"を介してにajaxのチェックボックスフォームを呼び出す場合、ajaxの動作は正常です
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
       'title' => 'Copy and Remove', 
       'page callback' => 'drupal_get_form', 
       'page arguments' => array('ajax_add_to_my_vocabulary', 2) , 
       'access arguments' => array('create my vocabulary'), 
      ); 
    }
    • 作成されたフォームに"<form>"要素およびajaxの要素が含まれているのでajaxが正常に動作します
      hook_menuが"drupal_get_form"を介してフォームを呼び出す場合に"<form>"要素が含まれてajaxが正常に動作

結論:表示系ページは直接に呼び出し、入力フォーム系は"drupal_get_form"を介して呼び出す

  • hook_menu()を実装時に、呼び出すページの性質によりページの呼び出す方法を決めます
    • 表示系ページ(データ更新なし)の場合は直接に呼び出せばよいです
    • 入力フォーム系のページは"drupal_get_form"を介して呼び出します
  • 表示系か、フォーム系かを区別せず、いつも"drupal_get_form"を介して呼び出しても構わないです
  • "drupal_get_form"がフォーム構築のプロセスが含まているので、フォームの要素構築するには必要です
    • drupa_get_form, drupal_prepare_form, drupal_retrieve_formの違い
    • これを使用する場合少し(どのぐらいは測定していないが)遅いかもしれないです
drupal
entity type
rules

Drupalのモジュール(EntityAPI)がモジュール(Rules、Viewsなど)に統合機能を提供していますが、各モジュール間の統一性が欠けています

  • 背景:Drupal7.56、EntityAPI7.x-1.8、Rules7.x-2.10
  • モジュール(EntityAPI)がほかのメジャーモジュール(Rules、Viewsなど)に統合機能を提供して、EntityAPIがよく利用されるようになります
  • ただし、各モジュール間の連携に統一性が欠けていることを気が付きました
    • 開発メンバーが違うので、開発ドキュメントが不備の場合、いろいろな誤解が生じるでしょう

例:エンティティ(Entity)へのアクセス権限管理にモジュール(Rules)が独自の権限("save")を生み出している

  • エンティティのアクセス権限チェック関数:entity_access($op, $entity_type, $entity = NULL, $account = NULL) (本家のentity_accessについての説明をご参考)
    • 引数$opは四つの権限:'view', 'update', 'create' or 'delete' (hook_entity_infoのパラメーター"access callback"を実装時にこの四つの権限を定義すればよいはずです)
  • モジュール(Rules)がエンティティアクセスチェック時に、"save"権限を生み出しています
    • rules/modules/entity_rules.incの313行あたモジュール(Rules)がエンティティに”save”権限を追加
    • 元々、モジュール(Rules)の"entity_save"は、「エンティティデータの保存」アクションです
    • ここで、"entity_save"を"save"に変換して、エンティティの"access callback"にパラメーターとして渡し、権限チェックをします
    • エンティティが定義している"access callback"に"save"権限を定義してなければ、権限がないと判断されます

対応方法1:モジュール(Rules)側でエンティティへのアクセス権限を再定義する

  • この例を限っては、モジュール(Rules)側で、エンティティへのアクセス権限の再定義します
    • Rulesの設定で「権限チェック省略」オプションがあるので、権限チェックを行わないことができます
    • Rulesの権限管理で各タイプのユーザーに権限を付与することもできます
      モジュール(Rules)でエンティティへのアクセス権限をユーザーに付与する設定

対応方法2:エンティティ側の"access callback"に"save"権限チェックを追加

  • hook_entity_info()の"access callback"の実装に、"save"権限を追加します("update"権限と同じはず)
    /**
     * Access callback for endity
     */
    function vocabulary_access($op, $entity, $account = NULL, $entity_type = NULL) {
        global $user;
    
        if (!isset($account)) {
            $account = $user;
        }
        switch ($op) {
            case 'create':
                return user_access('administer vocabulary', $account)
                || user_access('create vocabulary', $account);
            case 'view':
                return user_access('administer vocabulary', $account)
                || user_access('view vocabulary', $account);
            case 'edit':
            case 'save':    // save permission created by  entity_rule.inc
                return user_access('administer vocabulary')
                || user_access('edit any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
            case 'delete':
                return user_access('administer vocabulary')
                || user_access('delete any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
        }
    }
  • サードパーティーのモジュールでは、ソースコードの変更はしないほうがよいでしょう。対応方法1をお勧めします
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
form
menu

Drupalのhook_menu()がカスタムページ作成時によく使用されます

  • Drupalのサイトにページを追加する手段はいろいろあります
  • モジュールでページを追加し、メニューをGUIで設定することができます
    • モジュール(Views)で検索結果をページとして表示することができます(主に表示系のページ、入力フォームはなし)
    • モジュール(Panels)で各種要素を集めてページを作成します(主に表示系のページ、入力フォームなし)
    • モジュール(Web Form)でコンタクト/アンケートなどのフォームページを作成(フォーム系、入力して、データをサーバーに送信して保存)
  • プログラミングでページを作成する場合にhook_menu()を実装する必要があります
    • hook_menu()を実装時に、いろいろなパラメーター設定が紛らわしくて、注意する必要があります
    • Drupalのhook_menuでのURL設定にワイドカードの利用
    • Drupalのhook_menuでのユーザーロール別アクセス権限管理
    • 今回は、「page callback」パラメーターを詳細を説明します

Drupalのhook_menu()のパラメーター「page callback」が直接にページの呼び出しと「drupal_get_form」を介してページを呼び出す

  • ここは、ajaxで動作するチェックボックスを例として上げます
    • チェックボックスをクリックすると、ajaxでサーバーと交信する
      function ajax_add_to_my_vocabulary($form,&$form_state, $entity){
      
          global $user ;
      
          //  データがすでに存在するか否かのチェック
          $my_voca_result = db_query("SELECT entity_id FROM my_ch_vocabulary WHERE vid=".$entity->vid . " AND uid=".$user->uid)->fetchAll();
          //   ajaxコールバック時に使用するパラメーター
          $form_state['has_my_voca_result'] = count($my_voca_result)>0 ? true:false ;
          $form_state['ch_vocabulary'] = $entity ;  
      
          // ajaxのチェックボックスフォーム定義
          $form['ch_voca_form']=array(
              '#type' => 'checkbox',
              '#disabled' => $user->uid == 0 ? true : false ,
              '#title' => count($my_voca_result)>0 ? t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>') : t('<span id="ch-voca-check-op-'.$entity->vid.'">Add</span>'),
              '#default_value' => count($my_voca_result)>0 ? 1 : 0,
              '#ajax' => array(
                  'callback' => 'add_to_my_vocabulary_callback',
                  'wrapper' => 'ch-voca-check-op-'.$entity->vid,
                  'method' => 'replace',
                  'effect' => 'fade',
              ),
          );
          return $form ;
      }
      
      // ajaxのコールバック関数
      function add_to_my_vocabulary_in_views($form, $form_state){
      
          global $user;
      
          $entity = $form_state['ch_vocabulary'] ;
      
          // DBにデータがある場合に削除処理
          if( $form_state['has_my_voca_result'] ) {
              db_delete( 'my_ch_vocabulary' )->condition( 'vid', $entity->vid )->condition('uid', $user->uid )->execute();
              //  チェックボックスのラベルを「Add」に変更
              return t('<span id="ch-voca-check-op-'.$vid.'">Add</span>');
          }
      
          // データがなければ、ch_vocabuaryをmy_vocabuaryにコピー
          $my_voca = entity_create('my_ch_vocabulary', array('vid'=>$entity->vid));
          $my_voca_wrapper = entity_metadata_wrapper( 'my_ch_vocabulary', $my_voca );
          $my_voca_wrapper->vocabulary->set( $ch_voca->vocabulary ) ;
          ....   //各種フィールド、プロパティのセット
          $my_voca_wrapper->save();   // コピーされたデータの保存
      
          //  チェックボックスのラベルを「Remove」に変更
          return  t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>');
      }
  • hook_menu()が直接にajaxのチェックボックスフォームを呼び出す場合、ajaxの動作しません
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
            'title' => 'Copy and Remove',
            'page callback' => 'ajax_add_to_my_vocabulary',
            'page arguments' => array(array(), array(), 2) ,
            'access arguments' => array('create my vocabulary'),
      );
    }
    • ajaxのチェックボックスフォーム「ajax_add_to_my_vocabulary」が表示されますが、<form>要素がなくて、ajaxの動作はしません
      hook_menuが直接のフォームを呼び出す場合<form>要素が作成されずチェックボックスの動作はしない
  • hook_menu()が"drupal_get_form"を介してにajaxのチェックボックスフォームを呼び出す場合、ajaxの動作は正常です
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
       'title' => 'Copy and Remove', 
       'page callback' => 'drupal_get_form', 
       'page arguments' => array('ajax_add_to_my_vocabulary', 2) , 
       'access arguments' => array('create my vocabulary'), 
      ); 
    }
    • 作成されたフォームに"<form>"要素およびajaxの要素が含まれているのでajaxが正常に動作します
      hook_menuが"drupal_get_form"を介してフォームを呼び出す場合に"<form>"要素が含まれてajaxが正常に動作

結論:表示系ページは直接に呼び出し、入力フォーム系は"drupal_get_form"を介して呼び出す

  • hook_menu()を実装時に、呼び出すページの性質によりページの呼び出す方法を決めます
    • 表示系ページ(データ更新なし)の場合は直接に呼び出せばよいです
    • 入力フォーム系のページは"drupal_get_form"を介して呼び出します
  • 表示系か、フォーム系かを区別せず、いつも"drupal_get_form"を介して呼び出しても構わないです
  • "drupal_get_form"がフォーム構築のプロセスが含まているので、フォームの要素構築するには必要です
    • drupa_get_form, drupal_prepare_form, drupal_retrieve_formの違い
    • これを使用する場合少し(どのぐらいは測定していないが)遅いかもしれないです
drupal
entity type
rules

Drupalのモジュール(EntityAPI)がモジュール(Rules、Viewsなど)に統合機能を提供していますが、各モジュール間の統一性が欠けています

  • 背景:Drupal7.56、EntityAPI7.x-1.8、Rules7.x-2.10
  • モジュール(EntityAPI)がほかのメジャーモジュール(Rules、Viewsなど)に統合機能を提供して、EntityAPIがよく利用されるようになります
  • ただし、各モジュール間の連携に統一性が欠けていることを気が付きました
    • 開発メンバーが違うので、開発ドキュメントが不備の場合、いろいろな誤解が生じるでしょう

例:エンティティ(Entity)へのアクセス権限管理にモジュール(Rules)が独自の権限("save")を生み出している

  • エンティティのアクセス権限チェック関数:entity_access($op, $entity_type, $entity = NULL, $account = NULL) (本家のentity_accessについての説明をご参考)
    • 引数$opは四つの権限:'view', 'update', 'create' or 'delete' (hook_entity_infoのパラメーター"access callback"を実装時にこの四つの権限を定義すればよいはずです)
  • モジュール(Rules)がエンティティアクセスチェック時に、"save"権限を生み出しています
    • rules/modules/entity_rules.incの313行あたモジュール(Rules)がエンティティに”save”権限を追加
    • 元々、モジュール(Rules)の"entity_save"は、「エンティティデータの保存」アクションです
    • ここで、"entity_save"を"save"に変換して、エンティティの"access callback"にパラメーターとして渡し、権限チェックをします
    • エンティティが定義している"access callback"に"save"権限を定義してなければ、権限がないと判断されます

対応方法1:モジュール(Rules)側でエンティティへのアクセス権限を再定義する

  • この例を限っては、モジュール(Rules)側で、エンティティへのアクセス権限の再定義します
    • Rulesの設定で「権限チェック省略」オプションがあるので、権限チェックを行わないことができます
    • Rulesの権限管理で各タイプのユーザーに権限を付与することもできます
      モジュール(Rules)でエンティティへのアクセス権限をユーザーに付与する設定

対応方法2:エンティティ側の"access callback"に"save"権限チェックを追加

  • hook_entity_info()の"access callback"の実装に、"save"権限を追加します("update"権限と同じはず)
    /**
     * Access callback for endity
     */
    function vocabulary_access($op, $entity, $account = NULL, $entity_type = NULL) {
        global $user;
    
        if (!isset($account)) {
            $account = $user;
        }
        switch ($op) {
            case 'create':
                return user_access('administer vocabulary', $account)
                || user_access('create vocabulary', $account);
            case 'view':
                return user_access('administer vocabulary', $account)
                || user_access('view vocabulary', $account);
            case 'edit':
            case 'save':    // save permission created by  entity_rule.inc
                return user_access('administer vocabulary')
                || user_access('edit any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
            case 'delete':
                return user_access('administer vocabulary')
                || user_access('delete any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
        }
    }
  • サードパーティーのモジュールでは、ソースコードの変更はしないほうがよいでしょう。対応方法1をお勧めします
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
form
menu

Drupalのhook_menu()がカスタムページ作成時によく使用されます

  • Drupalのサイトにページを追加する手段はいろいろあります
  • モジュールでページを追加し、メニューをGUIで設定することができます
    • モジュール(Views)で検索結果をページとして表示することができます(主に表示系のページ、入力フォームはなし)
    • モジュール(Panels)で各種要素を集めてページを作成します(主に表示系のページ、入力フォームなし)
    • モジュール(Web Form)でコンタクト/アンケートなどのフォームページを作成(フォーム系、入力して、データをサーバーに送信して保存)
  • プログラミングでページを作成する場合にhook_menu()を実装する必要があります
    • hook_menu()を実装時に、いろいろなパラメーター設定が紛らわしくて、注意する必要があります
    • Drupalのhook_menuでのURL設定にワイドカードの利用
    • Drupalのhook_menuでのユーザーロール別アクセス権限管理
    • 今回は、「page callback」パラメーターを詳細を説明します

Drupalのhook_menu()のパラメーター「page callback」が直接にページの呼び出しと「drupal_get_form」を介してページを呼び出す

  • ここは、ajaxで動作するチェックボックスを例として上げます
    • チェックボックスをクリックすると、ajaxでサーバーと交信する
      function ajax_add_to_my_vocabulary($form,&$form_state, $entity){
      
          global $user ;
      
          //  データがすでに存在するか否かのチェック
          $my_voca_result = db_query("SELECT entity_id FROM my_ch_vocabulary WHERE vid=".$entity->vid . " AND uid=".$user->uid)->fetchAll();
          //   ajaxコールバック時に使用するパラメーター
          $form_state['has_my_voca_result'] = count($my_voca_result)>0 ? true:false ;
          $form_state['ch_vocabulary'] = $entity ;  
      
          // ajaxのチェックボックスフォーム定義
          $form['ch_voca_form']=array(
              '#type' => 'checkbox',
              '#disabled' => $user->uid == 0 ? true : false ,
              '#title' => count($my_voca_result)>0 ? t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>') : t('<span id="ch-voca-check-op-'.$entity->vid.'">Add</span>'),
              '#default_value' => count($my_voca_result)>0 ? 1 : 0,
              '#ajax' => array(
                  'callback' => 'add_to_my_vocabulary_callback',
                  'wrapper' => 'ch-voca-check-op-'.$entity->vid,
                  'method' => 'replace',
                  'effect' => 'fade',
              ),
          );
          return $form ;
      }
      
      // ajaxのコールバック関数
      function add_to_my_vocabulary_in_views($form, $form_state){
      
          global $user;
      
          $entity = $form_state['ch_vocabulary'] ;
      
          // DBにデータがある場合に削除処理
          if( $form_state['has_my_voca_result'] ) {
              db_delete( 'my_ch_vocabulary' )->condition( 'vid', $entity->vid )->condition('uid', $user->uid )->execute();
              //  チェックボックスのラベルを「Add」に変更
              return t('<span id="ch-voca-check-op-'.$vid.'">Add</span>');
          }
      
          // データがなければ、ch_vocabuaryをmy_vocabuaryにコピー
          $my_voca = entity_create('my_ch_vocabulary', array('vid'=>$entity->vid));
          $my_voca_wrapper = entity_metadata_wrapper( 'my_ch_vocabulary', $my_voca );
          $my_voca_wrapper->vocabulary->set( $ch_voca->vocabulary ) ;
          ....   //各種フィールド、プロパティのセット
          $my_voca_wrapper->save();   // コピーされたデータの保存
      
          //  チェックボックスのラベルを「Remove」に変更
          return  t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>');
      }
  • hook_menu()が直接にajaxのチェックボックスフォームを呼び出す場合、ajaxの動作しません
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
            'title' => 'Copy and Remove',
            'page callback' => 'ajax_add_to_my_vocabulary',
            'page arguments' => array(array(), array(), 2) ,
            'access arguments' => array('create my vocabulary'),
      );
    }
    • ajaxのチェックボックスフォーム「ajax_add_to_my_vocabulary」が表示されますが、<form>要素がなくて、ajaxの動作はしません
      hook_menuが直接のフォームを呼び出す場合<form>要素が作成されずチェックボックスの動作はしない
  • hook_menu()が"drupal_get_form"を介してにajaxのチェックボックスフォームを呼び出す場合、ajaxの動作は正常です
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
       'title' => 'Copy and Remove', 
       'page callback' => 'drupal_get_form', 
       'page arguments' => array('ajax_add_to_my_vocabulary', 2) , 
       'access arguments' => array('create my vocabulary'), 
      ); 
    }
    • 作成されたフォームに"<form>"要素およびajaxの要素が含まれているのでajaxが正常に動作します
      hook_menuが"drupal_get_form"を介してフォームを呼び出す場合に"<form>"要素が含まれてajaxが正常に動作

結論:表示系ページは直接に呼び出し、入力フォーム系は"drupal_get_form"を介して呼び出す

  • hook_menu()を実装時に、呼び出すページの性質によりページの呼び出す方法を決めます
    • 表示系ページ(データ更新なし)の場合は直接に呼び出せばよいです
    • 入力フォーム系のページは"drupal_get_form"を介して呼び出します
  • 表示系か、フォーム系かを区別せず、いつも"drupal_get_form"を介して呼び出しても構わないです
  • "drupal_get_form"がフォーム構築のプロセスが含まているので、フォームの要素構築するには必要です
    • drupa_get_form, drupal_prepare_form, drupal_retrieve_formの違い
    • これを使用する場合少し(どのぐらいは測定していないが)遅いかもしれないです
drupal
entity type
rules

Drupalのモジュール(EntityAPI)がモジュール(Rules、Viewsなど)に統合機能を提供していますが、各モジュール間の統一性が欠けています

  • 背景:Drupal7.56、EntityAPI7.x-1.8、Rules7.x-2.10
  • モジュール(EntityAPI)がほかのメジャーモジュール(Rules、Viewsなど)に統合機能を提供して、EntityAPIがよく利用されるようになります
  • ただし、各モジュール間の連携に統一性が欠けていることを気が付きました
    • 開発メンバーが違うので、開発ドキュメントが不備の場合、いろいろな誤解が生じるでしょう

例:エンティティ(Entity)へのアクセス権限管理にモジュール(Rules)が独自の権限("save")を生み出している

  • エンティティのアクセス権限チェック関数:entity_access($op, $entity_type, $entity = NULL, $account = NULL) (本家のentity_accessについての説明をご参考)
    • 引数$opは四つの権限:'view', 'update', 'create' or 'delete' (hook_entity_infoのパラメーター"access callback"を実装時にこの四つの権限を定義すればよいはずです)
  • モジュール(Rules)がエンティティアクセスチェック時に、"save"権限を生み出しています
    • rules/modules/entity_rules.incの313行あたモジュール(Rules)がエンティティに”save”権限を追加
    • 元々、モジュール(Rules)の"entity_save"は、「エンティティデータの保存」アクションです
    • ここで、"entity_save"を"save"に変換して、エンティティの"access callback"にパラメーターとして渡し、権限チェックをします
    • エンティティが定義している"access callback"に"save"権限を定義してなければ、権限がないと判断されます

対応方法1:モジュール(Rules)側でエンティティへのアクセス権限を再定義する

  • この例を限っては、モジュール(Rules)側で、エンティティへのアクセス権限の再定義します
    • Rulesの設定で「権限チェック省略」オプションがあるので、権限チェックを行わないことができます
    • Rulesの権限管理で各タイプのユーザーに権限を付与することもできます
      モジュール(Rules)でエンティティへのアクセス権限をユーザーに付与する設定

対応方法2:エンティティ側の"access callback"に"save"権限チェックを追加

  • hook_entity_info()の"access callback"の実装に、"save"権限を追加します("update"権限と同じはず)
    /**
     * Access callback for endity
     */
    function vocabulary_access($op, $entity, $account = NULL, $entity_type = NULL) {
        global $user;
    
        if (!isset($account)) {
            $account = $user;
        }
        switch ($op) {
            case 'create':
                return user_access('administer vocabulary', $account)
                || user_access('create vocabulary', $account);
            case 'view':
                return user_access('administer vocabulary', $account)
                || user_access('view vocabulary', $account);
            case 'edit':
            case 'save':    // save permission created by  entity_rule.inc
                return user_access('administer vocabulary')
                || user_access('edit any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
            case 'delete':
                return user_access('administer vocabulary')
                || user_access('delete any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
        }
    }
  • サードパーティーのモジュールでは、ソースコードの変更はしないほうがよいでしょう。対応方法1をお勧めします
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
form
menu

Drupalのhook_menu()がカスタムページ作成時によく使用されます

  • Drupalのサイトにページを追加する手段はいろいろあります
  • モジュールでページを追加し、メニューをGUIで設定することができます
    • モジュール(Views)で検索結果をページとして表示することができます(主に表示系のページ、入力フォームはなし)
    • モジュール(Panels)で各種要素を集めてページを作成します(主に表示系のページ、入力フォームなし)
    • モジュール(Web Form)でコンタクト/アンケートなどのフォームページを作成(フォーム系、入力して、データをサーバーに送信して保存)
  • プログラミングでページを作成する場合にhook_menu()を実装する必要があります
    • hook_menu()を実装時に、いろいろなパラメーター設定が紛らわしくて、注意する必要があります
    • Drupalのhook_menuでのURL設定にワイドカードの利用
    • Drupalのhook_menuでのユーザーロール別アクセス権限管理
    • 今回は、「page callback」パラメーターを詳細を説明します

Drupalのhook_menu()のパラメーター「page callback」が直接にページの呼び出しと「drupal_get_form」を介してページを呼び出す

  • ここは、ajaxで動作するチェックボックスを例として上げます
    • チェックボックスをクリックすると、ajaxでサーバーと交信する
      function ajax_add_to_my_vocabulary($form,&$form_state, $entity){
      
          global $user ;
      
          //  データがすでに存在するか否かのチェック
          $my_voca_result = db_query("SELECT entity_id FROM my_ch_vocabulary WHERE vid=".$entity->vid . " AND uid=".$user->uid)->fetchAll();
          //   ajaxコールバック時に使用するパラメーター
          $form_state['has_my_voca_result'] = count($my_voca_result)>0 ? true:false ;
          $form_state['ch_vocabulary'] = $entity ;  
      
          // ajaxのチェックボックスフォーム定義
          $form['ch_voca_form']=array(
              '#type' => 'checkbox',
              '#disabled' => $user->uid == 0 ? true : false ,
              '#title' => count($my_voca_result)>0 ? t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>') : t('<span id="ch-voca-check-op-'.$entity->vid.'">Add</span>'),
              '#default_value' => count($my_voca_result)>0 ? 1 : 0,
              '#ajax' => array(
                  'callback' => 'add_to_my_vocabulary_callback',
                  'wrapper' => 'ch-voca-check-op-'.$entity->vid,
                  'method' => 'replace',
                  'effect' => 'fade',
              ),
          );
          return $form ;
      }
      
      // ajaxのコールバック関数
      function add_to_my_vocabulary_in_views($form, $form_state){
      
          global $user;
      
          $entity = $form_state['ch_vocabulary'] ;
      
          // DBにデータがある場合に削除処理
          if( $form_state['has_my_voca_result'] ) {
              db_delete( 'my_ch_vocabulary' )->condition( 'vid', $entity->vid )->condition('uid', $user->uid )->execute();
              //  チェックボックスのラベルを「Add」に変更
              return t('<span id="ch-voca-check-op-'.$vid.'">Add</span>');
          }
      
          // データがなければ、ch_vocabuaryをmy_vocabuaryにコピー
          $my_voca = entity_create('my_ch_vocabulary', array('vid'=>$entity->vid));
          $my_voca_wrapper = entity_metadata_wrapper( 'my_ch_vocabulary', $my_voca );
          $my_voca_wrapper->vocabulary->set( $ch_voca->vocabulary ) ;
          ....   //各種フィールド、プロパティのセット
          $my_voca_wrapper->save();   // コピーされたデータの保存
      
          //  チェックボックスのラベルを「Remove」に変更
          return  t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>');
      }
  • hook_menu()が直接にajaxのチェックボックスフォームを呼び出す場合、ajaxの動作しません
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
            'title' => 'Copy and Remove',
            'page callback' => 'ajax_add_to_my_vocabulary',
            'page arguments' => array(array(), array(), 2) ,
            'access arguments' => array('create my vocabulary'),
      );
    }
    • ajaxのチェックボックスフォーム「ajax_add_to_my_vocabulary」が表示されますが、<form>要素がなくて、ajaxの動作はしません
      hook_menuが直接のフォームを呼び出す場合<form>要素が作成されずチェックボックスの動作はしない
  • hook_menu()が"drupal_get_form"を介してにajaxのチェックボックスフォームを呼び出す場合、ajaxの動作は正常です
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
       'title' => 'Copy and Remove', 
       'page callback' => 'drupal_get_form', 
       'page arguments' => array('ajax_add_to_my_vocabulary', 2) , 
       'access arguments' => array('create my vocabulary'), 
      ); 
    }
    • 作成されたフォームに"<form>"要素およびajaxの要素が含まれているのでajaxが正常に動作します
      hook_menuが"drupal_get_form"を介してフォームを呼び出す場合に"<form>"要素が含まれてajaxが正常に動作

結論:表示系ページは直接に呼び出し、入力フォーム系は"drupal_get_form"を介して呼び出す

  • hook_menu()を実装時に、呼び出すページの性質によりページの呼び出す方法を決めます
    • 表示系ページ(データ更新なし)の場合は直接に呼び出せばよいです
    • 入力フォーム系のページは"drupal_get_form"を介して呼び出します
  • 表示系か、フォーム系かを区別せず、いつも"drupal_get_form"を介して呼び出しても構わないです
  • "drupal_get_form"がフォーム構築のプロセスが含まているので、フォームの要素構築するには必要です
    • drupa_get_form, drupal_prepare_form, drupal_retrieve_formの違い
    • これを使用する場合少し(どのぐらいは測定していないが)遅いかもしれないです
drupal
entity type
rules

Drupalのモジュール(EntityAPI)がモジュール(Rules、Viewsなど)に統合機能を提供していますが、各モジュール間の統一性が欠けています

  • 背景:Drupal7.56、EntityAPI7.x-1.8、Rules7.x-2.10
  • モジュール(EntityAPI)がほかのメジャーモジュール(Rules、Viewsなど)に統合機能を提供して、EntityAPIがよく利用されるようになります
  • ただし、各モジュール間の連携に統一性が欠けていることを気が付きました
    • 開発メンバーが違うので、開発ドキュメントが不備の場合、いろいろな誤解が生じるでしょう

例:エンティティ(Entity)へのアクセス権限管理にモジュール(Rules)が独自の権限("save")を生み出している

  • エンティティのアクセス権限チェック関数:entity_access($op, $entity_type, $entity = NULL, $account = NULL) (本家のentity_accessについての説明をご参考)
    • 引数$opは四つの権限:'view', 'update', 'create' or 'delete' (hook_entity_infoのパラメーター"access callback"を実装時にこの四つの権限を定義すればよいはずです)
  • モジュール(Rules)がエンティティアクセスチェック時に、"save"権限を生み出しています
    • rules/modules/entity_rules.incの313行あたモジュール(Rules)がエンティティに”save”権限を追加
    • 元々、モジュール(Rules)の"entity_save"は、「エンティティデータの保存」アクションです
    • ここで、"entity_save"を"save"に変換して、エンティティの"access callback"にパラメーターとして渡し、権限チェックをします
    • エンティティが定義している"access callback"に"save"権限を定義してなければ、権限がないと判断されます

対応方法1:モジュール(Rules)側でエンティティへのアクセス権限を再定義する

  • この例を限っては、モジュール(Rules)側で、エンティティへのアクセス権限の再定義します
    • Rulesの設定で「権限チェック省略」オプションがあるので、権限チェックを行わないことができます
    • Rulesの権限管理で各タイプのユーザーに権限を付与することもできます
      モジュール(Rules)でエンティティへのアクセス権限をユーザーに付与する設定

対応方法2:エンティティ側の"access callback"に"save"権限チェックを追加

  • hook_entity_info()の"access callback"の実装に、"save"権限を追加します("update"権限と同じはず)
    /**
     * Access callback for endity
     */
    function vocabulary_access($op, $entity, $account = NULL, $entity_type = NULL) {
        global $user;
    
        if (!isset($account)) {
            $account = $user;
        }
        switch ($op) {
            case 'create':
                return user_access('administer vocabulary', $account)
                || user_access('create vocabulary', $account);
            case 'view':
                return user_access('administer vocabulary', $account)
                || user_access('view vocabulary', $account);
            case 'edit':
            case 'save':    // save permission created by  entity_rule.inc
                return user_access('administer vocabulary')
                || user_access('edit any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
            case 'delete':
                return user_access('administer vocabulary')
                || user_access('delete any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
        }
    }
  • サードパーティーのモジュールでは、ソースコードの変更はしないほうがよいでしょう。対応方法1をお勧めします
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
form
menu

Drupalのhook_menu()がカスタムページ作成時によく使用されます

  • Drupalのサイトにページを追加する手段はいろいろあります
  • モジュールでページを追加し、メニューをGUIで設定することができます
    • モジュール(Views)で検索結果をページとして表示することができます(主に表示系のページ、入力フォームはなし)
    • モジュール(Panels)で各種要素を集めてページを作成します(主に表示系のページ、入力フォームなし)
    • モジュール(Web Form)でコンタクト/アンケートなどのフォームページを作成(フォーム系、入力して、データをサーバーに送信して保存)
  • プログラミングでページを作成する場合にhook_menu()を実装する必要があります
    • hook_menu()を実装時に、いろいろなパラメーター設定が紛らわしくて、注意する必要があります
    • Drupalのhook_menuでのURL設定にワイドカードの利用
    • Drupalのhook_menuでのユーザーロール別アクセス権限管理
    • 今回は、「page callback」パラメーターを詳細を説明します

Drupalのhook_menu()のパラメーター「page callback」が直接にページの呼び出しと「drupal_get_form」を介してページを呼び出す

  • ここは、ajaxで動作するチェックボックスを例として上げます
    • チェックボックスをクリックすると、ajaxでサーバーと交信する
      function ajax_add_to_my_vocabulary($form,&$form_state, $entity){
      
          global $user ;
      
          //  データがすでに存在するか否かのチェック
          $my_voca_result = db_query("SELECT entity_id FROM my_ch_vocabulary WHERE vid=".$entity->vid . " AND uid=".$user->uid)->fetchAll();
          //   ajaxコールバック時に使用するパラメーター
          $form_state['has_my_voca_result'] = count($my_voca_result)>0 ? true:false ;
          $form_state['ch_vocabulary'] = $entity ;  
      
          // ajaxのチェックボックスフォーム定義
          $form['ch_voca_form']=array(
              '#type' => 'checkbox',
              '#disabled' => $user->uid == 0 ? true : false ,
              '#title' => count($my_voca_result)>0 ? t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>') : t('<span id="ch-voca-check-op-'.$entity->vid.'">Add</span>'),
              '#default_value' => count($my_voca_result)>0 ? 1 : 0,
              '#ajax' => array(
                  'callback' => 'add_to_my_vocabulary_callback',
                  'wrapper' => 'ch-voca-check-op-'.$entity->vid,
                  'method' => 'replace',
                  'effect' => 'fade',
              ),
          );
          return $form ;
      }
      
      // ajaxのコールバック関数
      function add_to_my_vocabulary_in_views($form, $form_state){
      
          global $user;
      
          $entity = $form_state['ch_vocabulary'] ;
      
          // DBにデータがある場合に削除処理
          if( $form_state['has_my_voca_result'] ) {
              db_delete( 'my_ch_vocabulary' )->condition( 'vid', $entity->vid )->condition('uid', $user->uid )->execute();
              //  チェックボックスのラベルを「Add」に変更
              return t('<span id="ch-voca-check-op-'.$vid.'">Add</span>');
          }
      
          // データがなければ、ch_vocabuaryをmy_vocabuaryにコピー
          $my_voca = entity_create('my_ch_vocabulary', array('vid'=>$entity->vid));
          $my_voca_wrapper = entity_metadata_wrapper( 'my_ch_vocabulary', $my_voca );
          $my_voca_wrapper->vocabulary->set( $ch_voca->vocabulary ) ;
          ....   //各種フィールド、プロパティのセット
          $my_voca_wrapper->save();   // コピーされたデータの保存
      
          //  チェックボックスのラベルを「Remove」に変更
          return  t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>');
      }
  • hook_menu()が直接にajaxのチェックボックスフォームを呼び出す場合、ajaxの動作しません
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
            'title' => 'Copy and Remove',
            'page callback' => 'ajax_add_to_my_vocabulary',
            'page arguments' => array(array(), array(), 2) ,
            'access arguments' => array('create my vocabulary'),
      );
    }
    • ajaxのチェックボックスフォーム「ajax_add_to_my_vocabulary」が表示されますが、<form>要素がなくて、ajaxの動作はしません
      hook_menuが直接のフォームを呼び出す場合<form>要素が作成されずチェックボックスの動作はしない
  • hook_menu()が"drupal_get_form"を介してにajaxのチェックボックスフォームを呼び出す場合、ajaxの動作は正常です
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
       'title' => 'Copy and Remove', 
       'page callback' => 'drupal_get_form', 
       'page arguments' => array('ajax_add_to_my_vocabulary', 2) , 
       'access arguments' => array('create my vocabulary'), 
      ); 
    }
    • 作成されたフォームに"<form>"要素およびajaxの要素が含まれているのでajaxが正常に動作します
      hook_menuが"drupal_get_form"を介してフォームを呼び出す場合に"<form>"要素が含まれてajaxが正常に動作

結論:表示系ページは直接に呼び出し、入力フォーム系は"drupal_get_form"を介して呼び出す

  • hook_menu()を実装時に、呼び出すページの性質によりページの呼び出す方法を決めます
    • 表示系ページ(データ更新なし)の場合は直接に呼び出せばよいです
    • 入力フォーム系のページは"drupal_get_form"を介して呼び出します
  • 表示系か、フォーム系かを区別せず、いつも"drupal_get_form"を介して呼び出しても構わないです
  • "drupal_get_form"がフォーム構築のプロセスが含まているので、フォームの要素構築するには必要です
    • drupa_get_form, drupal_prepare_form, drupal_retrieve_formの違い
    • これを使用する場合少し(どのぐらいは測定していないが)遅いかもしれないです
drupal
entity type
rules

Drupalのモジュール(EntityAPI)がモジュール(Rules、Viewsなど)に統合機能を提供していますが、各モジュール間の統一性が欠けています

  • 背景:Drupal7.56、EntityAPI7.x-1.8、Rules7.x-2.10
  • モジュール(EntityAPI)がほかのメジャーモジュール(Rules、Viewsなど)に統合機能を提供して、EntityAPIがよく利用されるようになります
  • ただし、各モジュール間の連携に統一性が欠けていることを気が付きました
    • 開発メンバーが違うので、開発ドキュメントが不備の場合、いろいろな誤解が生じるでしょう

例:エンティティ(Entity)へのアクセス権限管理にモジュール(Rules)が独自の権限("save")を生み出している

  • エンティティのアクセス権限チェック関数:entity_access($op, $entity_type, $entity = NULL, $account = NULL) (本家のentity_accessについての説明をご参考)
    • 引数$opは四つの権限:'view', 'update', 'create' or 'delete' (hook_entity_infoのパラメーター"access callback"を実装時にこの四つの権限を定義すればよいはずです)
  • モジュール(Rules)がエンティティアクセスチェック時に、"save"権限を生み出しています
    • rules/modules/entity_rules.incの313行あたモジュール(Rules)がエンティティに”save”権限を追加
    • 元々、モジュール(Rules)の"entity_save"は、「エンティティデータの保存」アクションです
    • ここで、"entity_save"を"save"に変換して、エンティティの"access callback"にパラメーターとして渡し、権限チェックをします
    • エンティティが定義している"access callback"に"save"権限を定義してなければ、権限がないと判断されます

対応方法1:モジュール(Rules)側でエンティティへのアクセス権限を再定義する

  • この例を限っては、モジュール(Rules)側で、エンティティへのアクセス権限の再定義します
    • Rulesの設定で「権限チェック省略」オプションがあるので、権限チェックを行わないことができます
    • Rulesの権限管理で各タイプのユーザーに権限を付与することもできます
      モジュール(Rules)でエンティティへのアクセス権限をユーザーに付与する設定

対応方法2:エンティティ側の"access callback"に"save"権限チェックを追加

  • hook_entity_info()の"access callback"の実装に、"save"権限を追加します("update"権限と同じはず)
    /**
     * Access callback for endity
     */
    function vocabulary_access($op, $entity, $account = NULL, $entity_type = NULL) {
        global $user;
    
        if (!isset($account)) {
            $account = $user;
        }
        switch ($op) {
            case 'create':
                return user_access('administer vocabulary', $account)
                || user_access('create vocabulary', $account);
            case 'view':
                return user_access('administer vocabulary', $account)
                || user_access('view vocabulary', $account);
            case 'edit':
            case 'save':    // save permission created by  entity_rule.inc
                return user_access('administer vocabulary')
                || user_access('edit any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
            case 'delete':
                return user_access('administer vocabulary')
                || user_access('delete any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
        }
    }
  • サードパーティーのモジュールでは、ソースコードの変更はしないほうがよいでしょう。対応方法1をお勧めします
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
form
menu

Drupalのhook_menu()がカスタムページ作成時によく使用されます

  • Drupalのサイトにページを追加する手段はいろいろあります
  • モジュールでページを追加し、メニューをGUIで設定することができます
    • モジュール(Views)で検索結果をページとして表示することができます(主に表示系のページ、入力フォームはなし)
    • モジュール(Panels)で各種要素を集めてページを作成します(主に表示系のページ、入力フォームなし)
    • モジュール(Web Form)でコンタクト/アンケートなどのフォームページを作成(フォーム系、入力して、データをサーバーに送信して保存)
  • プログラミングでページを作成する場合にhook_menu()を実装する必要があります
    • hook_menu()を実装時に、いろいろなパラメーター設定が紛らわしくて、注意する必要があります
    • Drupalのhook_menuでのURL設定にワイドカードの利用
    • Drupalのhook_menuでのユーザーロール別アクセス権限管理
    • 今回は、「page callback」パラメーターを詳細を説明します

Drupalのhook_menu()のパラメーター「page callback」が直接にページの呼び出しと「drupal_get_form」を介してページを呼び出す

  • ここは、ajaxで動作するチェックボックスを例として上げます
    • チェックボックスをクリックすると、ajaxでサーバーと交信する
      function ajax_add_to_my_vocabulary($form,&$form_state, $entity){
      
          global $user ;
      
          //  データがすでに存在するか否かのチェック
          $my_voca_result = db_query("SELECT entity_id FROM my_ch_vocabulary WHERE vid=".$entity->vid . " AND uid=".$user->uid)->fetchAll();
          //   ajaxコールバック時に使用するパラメーター
          $form_state['has_my_voca_result'] = count($my_voca_result)>0 ? true:false ;
          $form_state['ch_vocabulary'] = $entity ;  
      
          // ajaxのチェックボックスフォーム定義
          $form['ch_voca_form']=array(
              '#type' => 'checkbox',
              '#disabled' => $user->uid == 0 ? true : false ,
              '#title' => count($my_voca_result)>0 ? t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>') : t('<span id="ch-voca-check-op-'.$entity->vid.'">Add</span>'),
              '#default_value' => count($my_voca_result)>0 ? 1 : 0,
              '#ajax' => array(
                  'callback' => 'add_to_my_vocabulary_callback',
                  'wrapper' => 'ch-voca-check-op-'.$entity->vid,
                  'method' => 'replace',
                  'effect' => 'fade',
              ),
          );
          return $form ;
      }
      
      // ajaxのコールバック関数
      function add_to_my_vocabulary_in_views($form, $form_state){
      
          global $user;
      
          $entity = $form_state['ch_vocabulary'] ;
      
          // DBにデータがある場合に削除処理
          if( $form_state['has_my_voca_result'] ) {
              db_delete( 'my_ch_vocabulary' )->condition( 'vid', $entity->vid )->condition('uid', $user->uid )->execute();
              //  チェックボックスのラベルを「Add」に変更
              return t('<span id="ch-voca-check-op-'.$vid.'">Add</span>');
          }
      
          // データがなければ、ch_vocabuaryをmy_vocabuaryにコピー
          $my_voca = entity_create('my_ch_vocabulary', array('vid'=>$entity->vid));
          $my_voca_wrapper = entity_metadata_wrapper( 'my_ch_vocabulary', $my_voca );
          $my_voca_wrapper->vocabulary->set( $ch_voca->vocabulary ) ;
          ....   //各種フィールド、プロパティのセット
          $my_voca_wrapper->save();   // コピーされたデータの保存
      
          //  チェックボックスのラベルを「Remove」に変更
          return  t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>');
      }
  • hook_menu()が直接にajaxのチェックボックスフォームを呼び出す場合、ajaxの動作しません
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
            'title' => 'Copy and Remove',
            'page callback' => 'ajax_add_to_my_vocabulary',
            'page arguments' => array(array(), array(), 2) ,
            'access arguments' => array('create my vocabulary'),
      );
    }
    • ajaxのチェックボックスフォーム「ajax_add_to_my_vocabulary」が表示されますが、<form>要素がなくて、ajaxの動作はしません
      hook_menuが直接のフォームを呼び出す場合<form>要素が作成されずチェックボックスの動作はしない
  • hook_menu()が"drupal_get_form"を介してにajaxのチェックボックスフォームを呼び出す場合、ajaxの動作は正常です
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
       'title' => 'Copy and Remove', 
       'page callback' => 'drupal_get_form', 
       'page arguments' => array('ajax_add_to_my_vocabulary', 2) , 
       'access arguments' => array('create my vocabulary'), 
      ); 
    }
    • 作成されたフォームに"<form>"要素およびajaxの要素が含まれているのでajaxが正常に動作します
      hook_menuが"drupal_get_form"を介してフォームを呼び出す場合に"<form>"要素が含まれてajaxが正常に動作

結論:表示系ページは直接に呼び出し、入力フォーム系は"drupal_get_form"を介して呼び出す

  • hook_menu()を実装時に、呼び出すページの性質によりページの呼び出す方法を決めます
    • 表示系ページ(データ更新なし)の場合は直接に呼び出せばよいです
    • 入力フォーム系のページは"drupal_get_form"を介して呼び出します
  • 表示系か、フォーム系かを区別せず、いつも"drupal_get_form"を介して呼び出しても構わないです
  • "drupal_get_form"がフォーム構築のプロセスが含まているので、フォームの要素構築するには必要です
    • drupa_get_form, drupal_prepare_form, drupal_retrieve_formの違い
    • これを使用する場合少し(どのぐらいは測定していないが)遅いかもしれないです
drupal
entity type
rules

Drupalのモジュール(EntityAPI)がモジュール(Rules、Viewsなど)に統合機能を提供していますが、各モジュール間の統一性が欠けています

  • 背景:Drupal7.56、EntityAPI7.x-1.8、Rules7.x-2.10
  • モジュール(EntityAPI)がほかのメジャーモジュール(Rules、Viewsなど)に統合機能を提供して、EntityAPIがよく利用されるようになります
  • ただし、各モジュール間の連携に統一性が欠けていることを気が付きました
    • 開発メンバーが違うので、開発ドキュメントが不備の場合、いろいろな誤解が生じるでしょう

例:エンティティ(Entity)へのアクセス権限管理にモジュール(Rules)が独自の権限("save")を生み出している

  • エンティティのアクセス権限チェック関数:entity_access($op, $entity_type, $entity = NULL, $account = NULL) (本家のentity_accessについての説明をご参考)
    • 引数$opは四つの権限:'view', 'update', 'create' or 'delete' (hook_entity_infoのパラメーター"access callback"を実装時にこの四つの権限を定義すればよいはずです)
  • モジュール(Rules)がエンティティアクセスチェック時に、"save"権限を生み出しています
    • rules/modules/entity_rules.incの313行あたモジュール(Rules)がエンティティに”save”権限を追加
    • 元々、モジュール(Rules)の"entity_save"は、「エンティティデータの保存」アクションです
    • ここで、"entity_save"を"save"に変換して、エンティティの"access callback"にパラメーターとして渡し、権限チェックをします
    • エンティティが定義している"access callback"に"save"権限を定義してなければ、権限がないと判断されます

対応方法1:モジュール(Rules)側でエンティティへのアクセス権限を再定義する

  • この例を限っては、モジュール(Rules)側で、エンティティへのアクセス権限の再定義します
    • Rulesの設定で「権限チェック省略」オプションがあるので、権限チェックを行わないことができます
    • Rulesの権限管理で各タイプのユーザーに権限を付与することもできます
      モジュール(Rules)でエンティティへのアクセス権限をユーザーに付与する設定

対応方法2:エンティティ側の"access callback"に"save"権限チェックを追加

  • hook_entity_info()の"access callback"の実装に、"save"権限を追加します("update"権限と同じはず)
    /**
     * Access callback for endity
     */
    function vocabulary_access($op, $entity, $account = NULL, $entity_type = NULL) {
        global $user;
    
        if (!isset($account)) {
            $account = $user;
        }
        switch ($op) {
            case 'create':
                return user_access('administer vocabulary', $account)
                || user_access('create vocabulary', $account);
            case 'view':
                return user_access('administer vocabulary', $account)
                || user_access('view vocabulary', $account);
            case 'edit':
            case 'save':    // save permission created by  entity_rule.inc
                return user_access('administer vocabulary')
                || user_access('edit any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
            case 'delete':
                return user_access('administer vocabulary')
                || user_access('delete any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
        }
    }
  • サードパーティーのモジュールでは、ソースコードの変更はしないほうがよいでしょう。対応方法1をお勧めします
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
form
menu

Drupalのhook_menu()がカスタムページ作成時によく使用されます

  • Drupalのサイトにページを追加する手段はいろいろあります
  • モジュールでページを追加し、メニューをGUIで設定することができます
    • モジュール(Views)で検索結果をページとして表示することができます(主に表示系のページ、入力フォームはなし)
    • モジュール(Panels)で各種要素を集めてページを作成します(主に表示系のページ、入力フォームなし)
    • モジュール(Web Form)でコンタクト/アンケートなどのフォームページを作成(フォーム系、入力して、データをサーバーに送信して保存)
  • プログラミングでページを作成する場合にhook_menu()を実装する必要があります
    • hook_menu()を実装時に、いろいろなパラメーター設定が紛らわしくて、注意する必要があります
    • Drupalのhook_menuでのURL設定にワイドカードの利用
    • Drupalのhook_menuでのユーザーロール別アクセス権限管理
    • 今回は、「page callback」パラメーターを詳細を説明します

Drupalのhook_menu()のパラメーター「page callback」が直接にページの呼び出しと「drupal_get_form」を介してページを呼び出す

  • ここは、ajaxで動作するチェックボックスを例として上げます
    • チェックボックスをクリックすると、ajaxでサーバーと交信する
      function ajax_add_to_my_vocabulary($form,&$form_state, $entity){
      
          global $user ;
      
          //  データがすでに存在するか否かのチェック
          $my_voca_result = db_query("SELECT entity_id FROM my_ch_vocabulary WHERE vid=".$entity->vid . " AND uid=".$user->uid)->fetchAll();
          //   ajaxコールバック時に使用するパラメーター
          $form_state['has_my_voca_result'] = count($my_voca_result)>0 ? true:false ;
          $form_state['ch_vocabulary'] = $entity ;  
      
          // ajaxのチェックボックスフォーム定義
          $form['ch_voca_form']=array(
              '#type' => 'checkbox',
              '#disabled' => $user->uid == 0 ? true : false ,
              '#title' => count($my_voca_result)>0 ? t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>') : t('<span id="ch-voca-check-op-'.$entity->vid.'">Add</span>'),
              '#default_value' => count($my_voca_result)>0 ? 1 : 0,
              '#ajax' => array(
                  'callback' => 'add_to_my_vocabulary_callback',
                  'wrapper' => 'ch-voca-check-op-'.$entity->vid,
                  'method' => 'replace',
                  'effect' => 'fade',
              ),
          );
          return $form ;
      }
      
      // ajaxのコールバック関数
      function add_to_my_vocabulary_in_views($form, $form_state){
      
          global $user;
      
          $entity = $form_state['ch_vocabulary'] ;
      
          // DBにデータがある場合に削除処理
          if( $form_state['has_my_voca_result'] ) {
              db_delete( 'my_ch_vocabulary' )->condition( 'vid', $entity->vid )->condition('uid', $user->uid )->execute();
              //  チェックボックスのラベルを「Add」に変更
              return t('<span id="ch-voca-check-op-'.$vid.'">Add</span>');
          }
      
          // データがなければ、ch_vocabuaryをmy_vocabuaryにコピー
          $my_voca = entity_create('my_ch_vocabulary', array('vid'=>$entity->vid));
          $my_voca_wrapper = entity_metadata_wrapper( 'my_ch_vocabulary', $my_voca );
          $my_voca_wrapper->vocabulary->set( $ch_voca->vocabulary ) ;
          ....   //各種フィールド、プロパティのセット
          $my_voca_wrapper->save();   // コピーされたデータの保存
      
          //  チェックボックスのラベルを「Remove」に変更
          return  t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>');
      }
  • hook_menu()が直接にajaxのチェックボックスフォームを呼び出す場合、ajaxの動作しません
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
            'title' => 'Copy and Remove',
            'page callback' => 'ajax_add_to_my_vocabulary',
            'page arguments' => array(array(), array(), 2) ,
            'access arguments' => array('create my vocabulary'),
      );
    }
    • ajaxのチェックボックスフォーム「ajax_add_to_my_vocabulary」が表示されますが、<form>要素がなくて、ajaxの動作はしません
      hook_menuが直接のフォームを呼び出す場合<form>要素が作成されずチェックボックスの動作はしない
  • hook_menu()が"drupal_get_form"を介してにajaxのチェックボックスフォームを呼び出す場合、ajaxの動作は正常です
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
       'title' => 'Copy and Remove', 
       'page callback' => 'drupal_get_form', 
       'page arguments' => array('ajax_add_to_my_vocabulary', 2) , 
       'access arguments' => array('create my vocabulary'), 
      ); 
    }
    • 作成されたフォームに"<form>"要素およびajaxの要素が含まれているのでajaxが正常に動作します
      hook_menuが"drupal_get_form"を介してフォームを呼び出す場合に"<form>"要素が含まれてajaxが正常に動作

結論:表示系ページは直接に呼び出し、入力フォーム系は"drupal_get_form"を介して呼び出す

  • hook_menu()を実装時に、呼び出すページの性質によりページの呼び出す方法を決めます
    • 表示系ページ(データ更新なし)の場合は直接に呼び出せばよいです
    • 入力フォーム系のページは"drupal_get_form"を介して呼び出します
  • 表示系か、フォーム系かを区別せず、いつも"drupal_get_form"を介して呼び出しても構わないです
  • "drupal_get_form"がフォーム構築のプロセスが含まているので、フォームの要素構築するには必要です
    • drupa_get_form, drupal_prepare_form, drupal_retrieve_formの違い
    • これを使用する場合少し(どのぐらいは測定していないが)遅いかもしれないです
drupal
entity type
rules

Drupalのモジュール(EntityAPI)がモジュール(Rules、Viewsなど)に統合機能を提供していますが、各モジュール間の統一性が欠けています

  • 背景:Drupal7.56、EntityAPI7.x-1.8、Rules7.x-2.10
  • モジュール(EntityAPI)がほかのメジャーモジュール(Rules、Viewsなど)に統合機能を提供して、EntityAPIがよく利用されるようになります
  • ただし、各モジュール間の連携に統一性が欠けていることを気が付きました
    • 開発メンバーが違うので、開発ドキュメントが不備の場合、いろいろな誤解が生じるでしょう

例:エンティティ(Entity)へのアクセス権限管理にモジュール(Rules)が独自の権限("save")を生み出している

  • エンティティのアクセス権限チェック関数:entity_access($op, $entity_type, $entity = NULL, $account = NULL) (本家のentity_accessについての説明をご参考)
    • 引数$opは四つの権限:'view', 'update', 'create' or 'delete' (hook_entity_infoのパラメーター"access callback"を実装時にこの四つの権限を定義すればよいはずです)
  • モジュール(Rules)がエンティティアクセスチェック時に、"save"権限を生み出しています
    • rules/modules/entity_rules.incの313行あたモジュール(Rules)がエンティティに”save”権限を追加
    • 元々、モジュール(Rules)の"entity_save"は、「エンティティデータの保存」アクションです
    • ここで、"entity_save"を"save"に変換して、エンティティの"access callback"にパラメーターとして渡し、権限チェックをします
    • エンティティが定義している"access callback"に"save"権限を定義してなければ、権限がないと判断されます

対応方法1:モジュール(Rules)側でエンティティへのアクセス権限を再定義する

  • この例を限っては、モジュール(Rules)側で、エンティティへのアクセス権限の再定義します
    • Rulesの設定で「権限チェック省略」オプションがあるので、権限チェックを行わないことができます
    • Rulesの権限管理で各タイプのユーザーに権限を付与することもできます
      モジュール(Rules)でエンティティへのアクセス権限をユーザーに付与する設定

対応方法2:エンティティ側の"access callback"に"save"権限チェックを追加

  • hook_entity_info()の"access callback"の実装に、"save"権限を追加します("update"権限と同じはず)
    /**
     * Access callback for endity
     */
    function vocabulary_access($op, $entity, $account = NULL, $entity_type = NULL) {
        global $user;
    
        if (!isset($account)) {
            $account = $user;
        }
        switch ($op) {
            case 'create':
                return user_access('administer vocabulary', $account)
                || user_access('create vocabulary', $account);
            case 'view':
                return user_access('administer vocabulary', $account)
                || user_access('view vocabulary', $account);
            case 'edit':
            case 'save':    // save permission created by  entity_rule.inc
                return user_access('administer vocabulary')
                || user_access('edit any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
            case 'delete':
                return user_access('administer vocabulary')
                || user_access('delete any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
        }
    }
  • サードパーティーのモジュールでは、ソースコードの変更はしないほうがよいでしょう。対応方法1をお勧めします
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
form
menu

Drupalのhook_menu()がカスタムページ作成時によく使用されます

  • Drupalのサイトにページを追加する手段はいろいろあります
  • モジュールでページを追加し、メニューをGUIで設定することができます
    • モジュール(Views)で検索結果をページとして表示することができます(主に表示系のページ、入力フォームはなし)
    • モジュール(Panels)で各種要素を集めてページを作成します(主に表示系のページ、入力フォームなし)
    • モジュール(Web Form)でコンタクト/アンケートなどのフォームページを作成(フォーム系、入力して、データをサーバーに送信して保存)
  • プログラミングでページを作成する場合にhook_menu()を実装する必要があります
    • hook_menu()を実装時に、いろいろなパラメーター設定が紛らわしくて、注意する必要があります
    • Drupalのhook_menuでのURL設定にワイドカードの利用
    • Drupalのhook_menuでのユーザーロール別アクセス権限管理
    • 今回は、「page callback」パラメーターを詳細を説明します

Drupalのhook_menu()のパラメーター「page callback」が直接にページの呼び出しと「drupal_get_form」を介してページを呼び出す

  • ここは、ajaxで動作するチェックボックスを例として上げます
    • チェックボックスをクリックすると、ajaxでサーバーと交信する
      function ajax_add_to_my_vocabulary($form,&$form_state, $entity){
      
          global $user ;
      
          //  データがすでに存在するか否かのチェック
          $my_voca_result = db_query("SELECT entity_id FROM my_ch_vocabulary WHERE vid=".$entity->vid . " AND uid=".$user->uid)->fetchAll();
          //   ajaxコールバック時に使用するパラメーター
          $form_state['has_my_voca_result'] = count($my_voca_result)>0 ? true:false ;
          $form_state['ch_vocabulary'] = $entity ;  
      
          // ajaxのチェックボックスフォーム定義
          $form['ch_voca_form']=array(
              '#type' => 'checkbox',
              '#disabled' => $user->uid == 0 ? true : false ,
              '#title' => count($my_voca_result)>0 ? t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>') : t('<span id="ch-voca-check-op-'.$entity->vid.'">Add</span>'),
              '#default_value' => count($my_voca_result)>0 ? 1 : 0,
              '#ajax' => array(
                  'callback' => 'add_to_my_vocabulary_callback',
                  'wrapper' => 'ch-voca-check-op-'.$entity->vid,
                  'method' => 'replace',
                  'effect' => 'fade',
              ),
          );
          return $form ;
      }
      
      // ajaxのコールバック関数
      function add_to_my_vocabulary_in_views($form, $form_state){
      
          global $user;
      
          $entity = $form_state['ch_vocabulary'] ;
      
          // DBにデータがある場合に削除処理
          if( $form_state['has_my_voca_result'] ) {
              db_delete( 'my_ch_vocabulary' )->condition( 'vid', $entity->vid )->condition('uid', $user->uid )->execute();
              //  チェックボックスのラベルを「Add」に変更
              return t('<span id="ch-voca-check-op-'.$vid.'">Add</span>');
          }
      
          // データがなければ、ch_vocabuaryをmy_vocabuaryにコピー
          $my_voca = entity_create('my_ch_vocabulary', array('vid'=>$entity->vid));
          $my_voca_wrapper = entity_metadata_wrapper( 'my_ch_vocabulary', $my_voca );
          $my_voca_wrapper->vocabulary->set( $ch_voca->vocabulary ) ;
          ....   //各種フィールド、プロパティのセット
          $my_voca_wrapper->save();   // コピーされたデータの保存
      
          //  チェックボックスのラベルを「Remove」に変更
          return  t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>');
      }
  • hook_menu()が直接にajaxのチェックボックスフォームを呼び出す場合、ajaxの動作しません
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
            'title' => 'Copy and Remove',
            'page callback' => 'ajax_add_to_my_vocabulary',
            'page arguments' => array(array(), array(), 2) ,
            'access arguments' => array('create my vocabulary'),
      );
    }
    • ajaxのチェックボックスフォーム「ajax_add_to_my_vocabulary」が表示されますが、<form>要素がなくて、ajaxの動作はしません
      hook_menuが直接のフォームを呼び出す場合<form>要素が作成されずチェックボックスの動作はしない
  • hook_menu()が"drupal_get_form"を介してにajaxのチェックボックスフォームを呼び出す場合、ajaxの動作は正常です
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
       'title' => 'Copy and Remove', 
       'page callback' => 'drupal_get_form', 
       'page arguments' => array('ajax_add_to_my_vocabulary', 2) , 
       'access arguments' => array('create my vocabulary'), 
      ); 
    }
    • 作成されたフォームに"<form>"要素およびajaxの要素が含まれているのでajaxが正常に動作します
      hook_menuが"drupal_get_form"を介してフォームを呼び出す場合に"<form>"要素が含まれてajaxが正常に動作

結論:表示系ページは直接に呼び出し、入力フォーム系は"drupal_get_form"を介して呼び出す

  • hook_menu()を実装時に、呼び出すページの性質によりページの呼び出す方法を決めます
    • 表示系ページ(データ更新なし)の場合は直接に呼び出せばよいです
    • 入力フォーム系のページは"drupal_get_form"を介して呼び出します
  • 表示系か、フォーム系かを区別せず、いつも"drupal_get_form"を介して呼び出しても構わないです
  • "drupal_get_form"がフォーム構築のプロセスが含まているので、フォームの要素構築するには必要です
    • drupa_get_form, drupal_prepare_form, drupal_retrieve_formの違い
    • これを使用する場合少し(どのぐらいは測定していないが)遅いかもしれないです
drupal
entity type
rules

Drupalのモジュール(EntityAPI)がモジュール(Rules、Viewsなど)に統合機能を提供していますが、各モジュール間の統一性が欠けています

  • 背景:Drupal7.56、EntityAPI7.x-1.8、Rules7.x-2.10
  • モジュール(EntityAPI)がほかのメジャーモジュール(Rules、Viewsなど)に統合機能を提供して、EntityAPIがよく利用されるようになります
  • ただし、各モジュール間の連携に統一性が欠けていることを気が付きました
    • 開発メンバーが違うので、開発ドキュメントが不備の場合、いろいろな誤解が生じるでしょう

例:エンティティ(Entity)へのアクセス権限管理にモジュール(Rules)が独自の権限("save")を生み出している

  • エンティティのアクセス権限チェック関数:entity_access($op, $entity_type, $entity = NULL, $account = NULL) (本家のentity_accessについての説明をご参考)
    • 引数$opは四つの権限:'view', 'update', 'create' or 'delete' (hook_entity_infoのパラメーター"access callback"を実装時にこの四つの権限を定義すればよいはずです)
  • モジュール(Rules)がエンティティアクセスチェック時に、"save"権限を生み出しています
    • rules/modules/entity_rules.incの313行あたモジュール(Rules)がエンティティに”save”権限を追加
    • 元々、モジュール(Rules)の"entity_save"は、「エンティティデータの保存」アクションです
    • ここで、"entity_save"を"save"に変換して、エンティティの"access callback"にパラメーターとして渡し、権限チェックをします
    • エンティティが定義している"access callback"に"save"権限を定義してなければ、権限がないと判断されます

対応方法1:モジュール(Rules)側でエンティティへのアクセス権限を再定義する

  • この例を限っては、モジュール(Rules)側で、エンティティへのアクセス権限の再定義します
    • Rulesの設定で「権限チェック省略」オプションがあるので、権限チェックを行わないことができます
    • Rulesの権限管理で各タイプのユーザーに権限を付与することもできます
      モジュール(Rules)でエンティティへのアクセス権限をユーザーに付与する設定

対応方法2:エンティティ側の"access callback"に"save"権限チェックを追加

  • hook_entity_info()の"access callback"の実装に、"save"権限を追加します("update"権限と同じはず)
    /**
     * Access callback for endity
     */
    function vocabulary_access($op, $entity, $account = NULL, $entity_type = NULL) {
        global $user;
    
        if (!isset($account)) {
            $account = $user;
        }
        switch ($op) {
            case 'create':
                return user_access('administer vocabulary', $account)
                || user_access('create vocabulary', $account);
            case 'view':
                return user_access('administer vocabulary', $account)
                || user_access('view vocabulary', $account);
            case 'edit':
            case 'save':    // save permission created by  entity_rule.inc
                return user_access('administer vocabulary')
                || user_access('edit any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
            case 'delete':
                return user_access('administer vocabulary')
                || user_access('delete any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
        }
    }
  • サードパーティーのモジュールでは、ソースコードの変更はしないほうがよいでしょう。対応方法1をお勧めします
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
form
menu

Drupalのhook_menu()がカスタムページ作成時によく使用されます

  • Drupalのサイトにページを追加する手段はいろいろあります
  • モジュールでページを追加し、メニューをGUIで設定することができます
    • モジュール(Views)で検索結果をページとして表示することができます(主に表示系のページ、入力フォームはなし)
    • モジュール(Panels)で各種要素を集めてページを作成します(主に表示系のページ、入力フォームなし)
    • モジュール(Web Form)でコンタクト/アンケートなどのフォームページを作成(フォーム系、入力して、データをサーバーに送信して保存)
  • プログラミングでページを作成する場合にhook_menu()を実装する必要があります
    • hook_menu()を実装時に、いろいろなパラメーター設定が紛らわしくて、注意する必要があります
    • Drupalのhook_menuでのURL設定にワイドカードの利用
    • Drupalのhook_menuでのユーザーロール別アクセス権限管理
    • 今回は、「page callback」パラメーターを詳細を説明します

Drupalのhook_menu()のパラメーター「page callback」が直接にページの呼び出しと「drupal_get_form」を介してページを呼び出す

  • ここは、ajaxで動作するチェックボックスを例として上げます
    • チェックボックスをクリックすると、ajaxでサーバーと交信する
      function ajax_add_to_my_vocabulary($form,&$form_state, $entity){
      
          global $user ;
      
          //  データがすでに存在するか否かのチェック
          $my_voca_result = db_query("SELECT entity_id FROM my_ch_vocabulary WHERE vid=".$entity->vid . " AND uid=".$user->uid)->fetchAll();
          //   ajaxコールバック時に使用するパラメーター
          $form_state['has_my_voca_result'] = count($my_voca_result)>0 ? true:false ;
          $form_state['ch_vocabulary'] = $entity ;  
      
          // ajaxのチェックボックスフォーム定義
          $form['ch_voca_form']=array(
              '#type' => 'checkbox',
              '#disabled' => $user->uid == 0 ? true : false ,
              '#title' => count($my_voca_result)>0 ? t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>') : t('<span id="ch-voca-check-op-'.$entity->vid.'">Add</span>'),
              '#default_value' => count($my_voca_result)>0 ? 1 : 0,
              '#ajax' => array(
                  'callback' => 'add_to_my_vocabulary_callback',
                  'wrapper' => 'ch-voca-check-op-'.$entity->vid,
                  'method' => 'replace',
                  'effect' => 'fade',
              ),
          );
          return $form ;
      }
      
      // ajaxのコールバック関数
      function add_to_my_vocabulary_in_views($form, $form_state){
      
          global $user;
      
          $entity = $form_state['ch_vocabulary'] ;
      
          // DBにデータがある場合に削除処理
          if( $form_state['has_my_voca_result'] ) {
              db_delete( 'my_ch_vocabulary' )->condition( 'vid', $entity->vid )->condition('uid', $user->uid )->execute();
              //  チェックボックスのラベルを「Add」に変更
              return t('<span id="ch-voca-check-op-'.$vid.'">Add</span>');
          }
      
          // データがなければ、ch_vocabuaryをmy_vocabuaryにコピー
          $my_voca = entity_create('my_ch_vocabulary', array('vid'=>$entity->vid));
          $my_voca_wrapper = entity_metadata_wrapper( 'my_ch_vocabulary', $my_voca );
          $my_voca_wrapper->vocabulary->set( $ch_voca->vocabulary ) ;
          ....   //各種フィールド、プロパティのセット
          $my_voca_wrapper->save();   // コピーされたデータの保存
      
          //  チェックボックスのラベルを「Remove」に変更
          return  t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>');
      }
  • hook_menu()が直接にajaxのチェックボックスフォームを呼び出す場合、ajaxの動作しません
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
            'title' => 'Copy and Remove',
            'page callback' => 'ajax_add_to_my_vocabulary',
            'page arguments' => array(array(), array(), 2) ,
            'access arguments' => array('create my vocabulary'),
      );
    }
    • ajaxのチェックボックスフォーム「ajax_add_to_my_vocabulary」が表示されますが、<form>要素がなくて、ajaxの動作はしません
      hook_menuが直接のフォームを呼び出す場合<form>要素が作成されずチェックボックスの動作はしない
  • hook_menu()が"drupal_get_form"を介してにajaxのチェックボックスフォームを呼び出す場合、ajaxの動作は正常です
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
       'title' => 'Copy and Remove', 
       'page callback' => 'drupal_get_form', 
       'page arguments' => array('ajax_add_to_my_vocabulary', 2) , 
       'access arguments' => array('create my vocabulary'), 
      ); 
    }
    • 作成されたフォームに"<form>"要素およびajaxの要素が含まれているのでajaxが正常に動作します
      hook_menuが"drupal_get_form"を介してフォームを呼び出す場合に"<form>"要素が含まれてajaxが正常に動作

結論:表示系ページは直接に呼び出し、入力フォーム系は"drupal_get_form"を介して呼び出す

  • hook_menu()を実装時に、呼び出すページの性質によりページの呼び出す方法を決めます
    • 表示系ページ(データ更新なし)の場合は直接に呼び出せばよいです
    • 入力フォーム系のページは"drupal_get_form"を介して呼び出します
  • 表示系か、フォーム系かを区別せず、いつも"drupal_get_form"を介して呼び出しても構わないです
  • "drupal_get_form"がフォーム構築のプロセスが含まているので、フォームの要素構築するには必要です
    • drupa_get_form, drupal_prepare_form, drupal_retrieve_formの違い
    • これを使用する場合少し(どのぐらいは測定していないが)遅いかもしれないです
drupal
entity type
rules

Drupalのモジュール(EntityAPI)がモジュール(Rules、Viewsなど)に統合機能を提供していますが、各モジュール間の統一性が欠けています

  • 背景:Drupal7.56、EntityAPI7.x-1.8、Rules7.x-2.10
  • モジュール(EntityAPI)がほかのメジャーモジュール(Rules、Viewsなど)に統合機能を提供して、EntityAPIがよく利用されるようになります
  • ただし、各モジュール間の連携に統一性が欠けていることを気が付きました
    • 開発メンバーが違うので、開発ドキュメントが不備の場合、いろいろな誤解が生じるでしょう

例:エンティティ(Entity)へのアクセス権限管理にモジュール(Rules)が独自の権限("save")を生み出している

  • エンティティのアクセス権限チェック関数:entity_access($op, $entity_type, $entity = NULL, $account = NULL) (本家のentity_accessについての説明をご参考)
    • 引数$opは四つの権限:'view', 'update', 'create' or 'delete' (hook_entity_infoのパラメーター"access callback"を実装時にこの四つの権限を定義すればよいはずです)
  • モジュール(Rules)がエンティティアクセスチェック時に、"save"権限を生み出しています
    • rules/modules/entity_rules.incの313行あたモジュール(Rules)がエンティティに”save”権限を追加
    • 元々、モジュール(Rules)の"entity_save"は、「エンティティデータの保存」アクションです
    • ここで、"entity_save"を"save"に変換して、エンティティの"access callback"にパラメーターとして渡し、権限チェックをします
    • エンティティが定義している"access callback"に"save"権限を定義してなければ、権限がないと判断されます

対応方法1:モジュール(Rules)側でエンティティへのアクセス権限を再定義する

  • この例を限っては、モジュール(Rules)側で、エンティティへのアクセス権限の再定義します
    • Rulesの設定で「権限チェック省略」オプションがあるので、権限チェックを行わないことができます
    • Rulesの権限管理で各タイプのユーザーに権限を付与することもできます
      モジュール(Rules)でエンティティへのアクセス権限をユーザーに付与する設定

対応方法2:エンティティ側の"access callback"に"save"権限チェックを追加

  • hook_entity_info()の"access callback"の実装に、"save"権限を追加します("update"権限と同じはず)
    /**
     * Access callback for endity
     */
    function vocabulary_access($op, $entity, $account = NULL, $entity_type = NULL) {
        global $user;
    
        if (!isset($account)) {
            $account = $user;
        }
        switch ($op) {
            case 'create':
                return user_access('administer vocabulary', $account)
                || user_access('create vocabulary', $account);
            case 'view':
                return user_access('administer vocabulary', $account)
                || user_access('view vocabulary', $account);
            case 'edit':
            case 'save':    // save permission created by  entity_rule.inc
                return user_access('administer vocabulary')
                || user_access('edit any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
            case 'delete':
                return user_access('administer vocabulary')
                || user_access('delete any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
        }
    }
  • サードパーティーのモジュールでは、ソースコードの変更はしないほうがよいでしょう。対応方法1をお勧めします
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
form
menu

Drupalのhook_menu()がカスタムページ作成時によく使用されます

  • Drupalのサイトにページを追加する手段はいろいろあります
  • モジュールでページを追加し、メニューをGUIで設定することができます
    • モジュール(Views)で検索結果をページとして表示することができます(主に表示系のページ、入力フォームはなし)
    • モジュール(Panels)で各種要素を集めてページを作成します(主に表示系のページ、入力フォームなし)
    • モジュール(Web Form)でコンタクト/アンケートなどのフォームページを作成(フォーム系、入力して、データをサーバーに送信して保存)
  • プログラミングでページを作成する場合にhook_menu()を実装する必要があります
    • hook_menu()を実装時に、いろいろなパラメーター設定が紛らわしくて、注意する必要があります
    • Drupalのhook_menuでのURL設定にワイドカードの利用
    • Drupalのhook_menuでのユーザーロール別アクセス権限管理
    • 今回は、「page callback」パラメーターを詳細を説明します

Drupalのhook_menu()のパラメーター「page callback」が直接にページの呼び出しと「drupal_get_form」を介してページを呼び出す

  • ここは、ajaxで動作するチェックボックスを例として上げます
    • チェックボックスをクリックすると、ajaxでサーバーと交信する
      function ajax_add_to_my_vocabulary($form,&$form_state, $entity){
      
          global $user ;
      
          //  データがすでに存在するか否かのチェック
          $my_voca_result = db_query("SELECT entity_id FROM my_ch_vocabulary WHERE vid=".$entity->vid . " AND uid=".$user->uid)->fetchAll();
          //   ajaxコールバック時に使用するパラメーター
          $form_state['has_my_voca_result'] = count($my_voca_result)>0 ? true:false ;
          $form_state['ch_vocabulary'] = $entity ;  
      
          // ajaxのチェックボックスフォーム定義
          $form['ch_voca_form']=array(
              '#type' => 'checkbox',
              '#disabled' => $user->uid == 0 ? true : false ,
              '#title' => count($my_voca_result)>0 ? t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>') : t('<span id="ch-voca-check-op-'.$entity->vid.'">Add</span>'),
              '#default_value' => count($my_voca_result)>0 ? 1 : 0,
              '#ajax' => array(
                  'callback' => 'add_to_my_vocabulary_callback',
                  'wrapper' => 'ch-voca-check-op-'.$entity->vid,
                  'method' => 'replace',
                  'effect' => 'fade',
              ),
          );
          return $form ;
      }
      
      // ajaxのコールバック関数
      function add_to_my_vocabulary_in_views($form, $form_state){
      
          global $user;
      
          $entity = $form_state['ch_vocabulary'] ;
      
          // DBにデータがある場合に削除処理
          if( $form_state['has_my_voca_result'] ) {
              db_delete( 'my_ch_vocabulary' )->condition( 'vid', $entity->vid )->condition('uid', $user->uid )->execute();
              //  チェックボックスのラベルを「Add」に変更
              return t('<span id="ch-voca-check-op-'.$vid.'">Add</span>');
          }
      
          // データがなければ、ch_vocabuaryをmy_vocabuaryにコピー
          $my_voca = entity_create('my_ch_vocabulary', array('vid'=>$entity->vid));
          $my_voca_wrapper = entity_metadata_wrapper( 'my_ch_vocabulary', $my_voca );
          $my_voca_wrapper->vocabulary->set( $ch_voca->vocabulary ) ;
          ....   //各種フィールド、プロパティのセット
          $my_voca_wrapper->save();   // コピーされたデータの保存
      
          //  チェックボックスのラベルを「Remove」に変更
          return  t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>');
      }
  • hook_menu()が直接にajaxのチェックボックスフォームを呼び出す場合、ajaxの動作しません
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
            'title' => 'Copy and Remove',
            'page callback' => 'ajax_add_to_my_vocabulary',
            'page arguments' => array(array(), array(), 2) ,
            'access arguments' => array('create my vocabulary'),
      );
    }
    • ajaxのチェックボックスフォーム「ajax_add_to_my_vocabulary」が表示されますが、<form>要素がなくて、ajaxの動作はしません
      hook_menuが直接のフォームを呼び出す場合<form>要素が作成されずチェックボックスの動作はしない
  • hook_menu()が"drupal_get_form"を介してにajaxのチェックボックスフォームを呼び出す場合、ajaxの動作は正常です
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
       'title' => 'Copy and Remove', 
       'page callback' => 'drupal_get_form', 
       'page arguments' => array('ajax_add_to_my_vocabulary', 2) , 
       'access arguments' => array('create my vocabulary'), 
      ); 
    }
    • 作成されたフォームに"<form>"要素およびajaxの要素が含まれているのでajaxが正常に動作します
      hook_menuが"drupal_get_form"を介してフォームを呼び出す場合に"<form>"要素が含まれてajaxが正常に動作

結論:表示系ページは直接に呼び出し、入力フォーム系は"drupal_get_form"を介して呼び出す

  • hook_menu()を実装時に、呼び出すページの性質によりページの呼び出す方法を決めます
    • 表示系ページ(データ更新なし)の場合は直接に呼び出せばよいです
    • 入力フォーム系のページは"drupal_get_form"を介して呼び出します
  • 表示系か、フォーム系かを区別せず、いつも"drupal_get_form"を介して呼び出しても構わないです
  • "drupal_get_form"がフォーム構築のプロセスが含まているので、フォームの要素構築するには必要です
    • drupa_get_form, drupal_prepare_form, drupal_retrieve_formの違い
    • これを使用する場合少し(どのぐらいは測定していないが)遅いかもしれないです
drupal
entity type
rules

Drupalのモジュール(EntityAPI)がモジュール(Rules、Viewsなど)に統合機能を提供していますが、各モジュール間の統一性が欠けています

  • 背景:Drupal7.56、EntityAPI7.x-1.8、Rules7.x-2.10
  • モジュール(EntityAPI)がほかのメジャーモジュール(Rules、Viewsなど)に統合機能を提供して、EntityAPIがよく利用されるようになります
  • ただし、各モジュール間の連携に統一性が欠けていることを気が付きました
    • 開発メンバーが違うので、開発ドキュメントが不備の場合、いろいろな誤解が生じるでしょう

例:エンティティ(Entity)へのアクセス権限管理にモジュール(Rules)が独自の権限("save")を生み出している

  • エンティティのアクセス権限チェック関数:entity_access($op, $entity_type, $entity = NULL, $account = NULL) (本家のentity_accessについての説明をご参考)
    • 引数$opは四つの権限:'view', 'update', 'create' or 'delete' (hook_entity_infoのパラメーター"access callback"を実装時にこの四つの権限を定義すればよいはずです)
  • モジュール(Rules)がエンティティアクセスチェック時に、"save"権限を生み出しています
    • rules/modules/entity_rules.incの313行あたモジュール(Rules)がエンティティに”save”権限を追加
    • 元々、モジュール(Rules)の"entity_save"は、「エンティティデータの保存」アクションです
    • ここで、"entity_save"を"save"に変換して、エンティティの"access callback"にパラメーターとして渡し、権限チェックをします
    • エンティティが定義している"access callback"に"save"権限を定義してなければ、権限がないと判断されます

対応方法1:モジュール(Rules)側でエンティティへのアクセス権限を再定義する

  • この例を限っては、モジュール(Rules)側で、エンティティへのアクセス権限の再定義します
    • Rulesの設定で「権限チェック省略」オプションがあるので、権限チェックを行わないことができます
    • Rulesの権限管理で各タイプのユーザーに権限を付与することもできます
      モジュール(Rules)でエンティティへのアクセス権限をユーザーに付与する設定

対応方法2:エンティティ側の"access callback"に"save"権限チェックを追加

  • hook_entity_info()の"access callback"の実装に、"save"権限を追加します("update"権限と同じはず)
    /**
     * Access callback for endity
     */
    function vocabulary_access($op, $entity, $account = NULL, $entity_type = NULL) {
        global $user;
    
        if (!isset($account)) {
            $account = $user;
        }
        switch ($op) {
            case 'create':
                return user_access('administer vocabulary', $account)
                || user_access('create vocabulary', $account);
            case 'view':
                return user_access('administer vocabulary', $account)
                || user_access('view vocabulary', $account);
            case 'edit':
            case 'save':    // save permission created by  entity_rule.inc
                return user_access('administer vocabulary')
                || user_access('edit any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
            case 'delete':
                return user_access('administer vocabulary')
                || user_access('delete any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
        }
    }
  • サードパーティーのモジュールでは、ソースコードの変更はしないほうがよいでしょう。対応方法1をお勧めします
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
form
menu

Drupalのhook_menu()がカスタムページ作成時によく使用されます

  • Drupalのサイトにページを追加する手段はいろいろあります
  • モジュールでページを追加し、メニューをGUIで設定することができます
    • モジュール(Views)で検索結果をページとして表示することができます(主に表示系のページ、入力フォームはなし)
    • モジュール(Panels)で各種要素を集めてページを作成します(主に表示系のページ、入力フォームなし)
    • モジュール(Web Form)でコンタクト/アンケートなどのフォームページを作成(フォーム系、入力して、データをサーバーに送信して保存)
  • プログラミングでページを作成する場合にhook_menu()を実装する必要があります
    • hook_menu()を実装時に、いろいろなパラメーター設定が紛らわしくて、注意する必要があります
    • Drupalのhook_menuでのURL設定にワイドカードの利用
    • Drupalのhook_menuでのユーザーロール別アクセス権限管理
    • 今回は、「page callback」パラメーターを詳細を説明します

Drupalのhook_menu()のパラメーター「page callback」が直接にページの呼び出しと「drupal_get_form」を介してページを呼び出す

  • ここは、ajaxで動作するチェックボックスを例として上げます
    • チェックボックスをクリックすると、ajaxでサーバーと交信する
      function ajax_add_to_my_vocabulary($form,&$form_state, $entity){
      
          global $user ;
      
          //  データがすでに存在するか否かのチェック
          $my_voca_result = db_query("SELECT entity_id FROM my_ch_vocabulary WHERE vid=".$entity->vid . " AND uid=".$user->uid)->fetchAll();
          //   ajaxコールバック時に使用するパラメーター
          $form_state['has_my_voca_result'] = count($my_voca_result)>0 ? true:false ;
          $form_state['ch_vocabulary'] = $entity ;  
      
          // ajaxのチェックボックスフォーム定義
          $form['ch_voca_form']=array(
              '#type' => 'checkbox',
              '#disabled' => $user->uid == 0 ? true : false ,
              '#title' => count($my_voca_result)>0 ? t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>') : t('<span id="ch-voca-check-op-'.$entity->vid.'">Add</span>'),
              '#default_value' => count($my_voca_result)>0 ? 1 : 0,
              '#ajax' => array(
                  'callback' => 'add_to_my_vocabulary_callback',
                  'wrapper' => 'ch-voca-check-op-'.$entity->vid,
                  'method' => 'replace',
                  'effect' => 'fade',
              ),
          );
          return $form ;
      }
      
      // ajaxのコールバック関数
      function add_to_my_vocabulary_in_views($form, $form_state){
      
          global $user;
      
          $entity = $form_state['ch_vocabulary'] ;
      
          // DBにデータがある場合に削除処理
          if( $form_state['has_my_voca_result'] ) {
              db_delete( 'my_ch_vocabulary' )->condition( 'vid', $entity->vid )->condition('uid', $user->uid )->execute();
              //  チェックボックスのラベルを「Add」に変更
              return t('<span id="ch-voca-check-op-'.$vid.'">Add</span>');
          }
      
          // データがなければ、ch_vocabuaryをmy_vocabuaryにコピー
          $my_voca = entity_create('my_ch_vocabulary', array('vid'=>$entity->vid));
          $my_voca_wrapper = entity_metadata_wrapper( 'my_ch_vocabulary', $my_voca );
          $my_voca_wrapper->vocabulary->set( $ch_voca->vocabulary ) ;
          ....   //各種フィールド、プロパティのセット
          $my_voca_wrapper->save();   // コピーされたデータの保存
      
          //  チェックボックスのラベルを「Remove」に変更
          return  t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>');
      }
  • hook_menu()が直接にajaxのチェックボックスフォームを呼び出す場合、ajaxの動作しません
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
            'title' => 'Copy and Remove',
            'page callback' => 'ajax_add_to_my_vocabulary',
            'page arguments' => array(array(), array(), 2) ,
            'access arguments' => array('create my vocabulary'),
      );
    }
    • ajaxのチェックボックスフォーム「ajax_add_to_my_vocabulary」が表示されますが、<form>要素がなくて、ajaxの動作はしません
      hook_menuが直接のフォームを呼び出す場合<form>要素が作成されずチェックボックスの動作はしない
  • hook_menu()が"drupal_get_form"を介してにajaxのチェックボックスフォームを呼び出す場合、ajaxの動作は正常です
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
       'title' => 'Copy and Remove', 
       'page callback' => 'drupal_get_form', 
       'page arguments' => array('ajax_add_to_my_vocabulary', 2) , 
       'access arguments' => array('create my vocabulary'), 
      ); 
    }
    • 作成されたフォームに"<form>"要素およびajaxの要素が含まれているのでajaxが正常に動作します
      hook_menuが"drupal_get_form"を介してフォームを呼び出す場合に"<form>"要素が含まれてajaxが正常に動作

結論:表示系ページは直接に呼び出し、入力フォーム系は"drupal_get_form"を介して呼び出す

  • hook_menu()を実装時に、呼び出すページの性質によりページの呼び出す方法を決めます
    • 表示系ページ(データ更新なし)の場合は直接に呼び出せばよいです
    • 入力フォーム系のページは"drupal_get_form"を介して呼び出します
  • 表示系か、フォーム系かを区別せず、いつも"drupal_get_form"を介して呼び出しても構わないです
  • "drupal_get_form"がフォーム構築のプロセスが含まているので、フォームの要素構築するには必要です
    • drupa_get_form, drupal_prepare_form, drupal_retrieve_formの違い
    • これを使用する場合少し(どのぐらいは測定していないが)遅いかもしれないです
drupal
entity type
rules

Drupalのモジュール(EntityAPI)がモジュール(Rules、Viewsなど)に統合機能を提供していますが、各モジュール間の統一性が欠けています

  • 背景:Drupal7.56、EntityAPI7.x-1.8、Rules7.x-2.10
  • モジュール(EntityAPI)がほかのメジャーモジュール(Rules、Viewsなど)に統合機能を提供して、EntityAPIがよく利用されるようになります
  • ただし、各モジュール間の連携に統一性が欠けていることを気が付きました
    • 開発メンバーが違うので、開発ドキュメントが不備の場合、いろいろな誤解が生じるでしょう

例:エンティティ(Entity)へのアクセス権限管理にモジュール(Rules)が独自の権限("save")を生み出している

  • エンティティのアクセス権限チェック関数:entity_access($op, $entity_type, $entity = NULL, $account = NULL) (本家のentity_accessについての説明をご参考)
    • 引数$opは四つの権限:'view', 'update', 'create' or 'delete' (hook_entity_infoのパラメーター"access callback"を実装時にこの四つの権限を定義すればよいはずです)
  • モジュール(Rules)がエンティティアクセスチェック時に、"save"権限を生み出しています
    • rules/modules/entity_rules.incの313行あたモジュール(Rules)がエンティティに”save”権限を追加
    • 元々、モジュール(Rules)の"entity_save"は、「エンティティデータの保存」アクションです
    • ここで、"entity_save"を"save"に変換して、エンティティの"access callback"にパラメーターとして渡し、権限チェックをします
    • エンティティが定義している"access callback"に"save"権限を定義してなければ、権限がないと判断されます

対応方法1:モジュール(Rules)側でエンティティへのアクセス権限を再定義する

  • この例を限っては、モジュール(Rules)側で、エンティティへのアクセス権限の再定義します
    • Rulesの設定で「権限チェック省略」オプションがあるので、権限チェックを行わないことができます
    • Rulesの権限管理で各タイプのユーザーに権限を付与することもできます
      モジュール(Rules)でエンティティへのアクセス権限をユーザーに付与する設定

対応方法2:エンティティ側の"access callback"に"save"権限チェックを追加

  • hook_entity_info()の"access callback"の実装に、"save"権限を追加します("update"権限と同じはず)
    /**
     * Access callback for endity
     */
    function vocabulary_access($op, $entity, $account = NULL, $entity_type = NULL) {
        global $user;
    
        if (!isset($account)) {
            $account = $user;
        }
        switch ($op) {
            case 'create':
                return user_access('administer vocabulary', $account)
                || user_access('create vocabulary', $account);
            case 'view':
                return user_access('administer vocabulary', $account)
                || user_access('view vocabulary', $account);
            case 'edit':
            case 'save':    // save permission created by  entity_rule.inc
                return user_access('administer vocabulary')
                || user_access('edit any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
            case 'delete':
                return user_access('administer vocabulary')
                || user_access('delete any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
        }
    }
  • サードパーティーのモジュールでは、ソースコードの変更はしないほうがよいでしょう。対応方法1をお勧めします
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
form
menu

Drupalのhook_menu()がカスタムページ作成時によく使用されます

  • Drupalのサイトにページを追加する手段はいろいろあります
  • モジュールでページを追加し、メニューをGUIで設定することができます
    • モジュール(Views)で検索結果をページとして表示することができます(主に表示系のページ、入力フォームはなし)
    • モジュール(Panels)で各種要素を集めてページを作成します(主に表示系のページ、入力フォームなし)
    • モジュール(Web Form)でコンタクト/アンケートなどのフォームページを作成(フォーム系、入力して、データをサーバーに送信して保存)
  • プログラミングでページを作成する場合にhook_menu()を実装する必要があります
    • hook_menu()を実装時に、いろいろなパラメーター設定が紛らわしくて、注意する必要があります
    • Drupalのhook_menuでのURL設定にワイドカードの利用
    • Drupalのhook_menuでのユーザーロール別アクセス権限管理
    • 今回は、「page callback」パラメーターを詳細を説明します

Drupalのhook_menu()のパラメーター「page callback」が直接にページの呼び出しと「drupal_get_form」を介してページを呼び出す

  • ここは、ajaxで動作するチェックボックスを例として上げます
    • チェックボックスをクリックすると、ajaxでサーバーと交信する
      function ajax_add_to_my_vocabulary($form,&$form_state, $entity){
      
          global $user ;
      
          //  データがすでに存在するか否かのチェック
          $my_voca_result = db_query("SELECT entity_id FROM my_ch_vocabulary WHERE vid=".$entity->vid . " AND uid=".$user->uid)->fetchAll();
          //   ajaxコールバック時に使用するパラメーター
          $form_state['has_my_voca_result'] = count($my_voca_result)>0 ? true:false ;
          $form_state['ch_vocabulary'] = $entity ;  
      
          // ajaxのチェックボックスフォーム定義
          $form['ch_voca_form']=array(
              '#type' => 'checkbox',
              '#disabled' => $user->uid == 0 ? true : false ,
              '#title' => count($my_voca_result)>0 ? t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>') : t('<span id="ch-voca-check-op-'.$entity->vid.'">Add</span>'),
              '#default_value' => count($my_voca_result)>0 ? 1 : 0,
              '#ajax' => array(
                  'callback' => 'add_to_my_vocabulary_callback',
                  'wrapper' => 'ch-voca-check-op-'.$entity->vid,
                  'method' => 'replace',
                  'effect' => 'fade',
              ),
          );
          return $form ;
      }
      
      // ajaxのコールバック関数
      function add_to_my_vocabulary_in_views($form, $form_state){
      
          global $user;
      
          $entity = $form_state['ch_vocabulary'] ;
      
          // DBにデータがある場合に削除処理
          if( $form_state['has_my_voca_result'] ) {
              db_delete( 'my_ch_vocabulary' )->condition( 'vid', $entity->vid )->condition('uid', $user->uid )->execute();
              //  チェックボックスのラベルを「Add」に変更
              return t('<span id="ch-voca-check-op-'.$vid.'">Add</span>');
          }
      
          // データがなければ、ch_vocabuaryをmy_vocabuaryにコピー
          $my_voca = entity_create('my_ch_vocabulary', array('vid'=>$entity->vid));
          $my_voca_wrapper = entity_metadata_wrapper( 'my_ch_vocabulary', $my_voca );
          $my_voca_wrapper->vocabulary->set( $ch_voca->vocabulary ) ;
          ....   //各種フィールド、プロパティのセット
          $my_voca_wrapper->save();   // コピーされたデータの保存
      
          //  チェックボックスのラベルを「Remove」に変更
          return  t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>');
      }
  • hook_menu()が直接にajaxのチェックボックスフォームを呼び出す場合、ajaxの動作しません
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
            'title' => 'Copy and Remove',
            'page callback' => 'ajax_add_to_my_vocabulary',
            'page arguments' => array(array(), array(), 2) ,
            'access arguments' => array('create my vocabulary'),
      );
    }
    • ajaxのチェックボックスフォーム「ajax_add_to_my_vocabulary」が表示されますが、<form>要素がなくて、ajaxの動作はしません
      hook_menuが直接のフォームを呼び出す場合<form>要素が作成されずチェックボックスの動作はしない
  • hook_menu()が"drupal_get_form"を介してにajaxのチェックボックスフォームを呼び出す場合、ajaxの動作は正常です
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
       'title' => 'Copy and Remove', 
       'page callback' => 'drupal_get_form', 
       'page arguments' => array('ajax_add_to_my_vocabulary', 2) , 
       'access arguments' => array('create my vocabulary'), 
      ); 
    }
    • 作成されたフォームに"<form>"要素およびajaxの要素が含まれているのでajaxが正常に動作します
      hook_menuが"drupal_get_form"を介してフォームを呼び出す場合に"<form>"要素が含まれてajaxが正常に動作

結論:表示系ページは直接に呼び出し、入力フォーム系は"drupal_get_form"を介して呼び出す

  • hook_menu()を実装時に、呼び出すページの性質によりページの呼び出す方法を決めます
    • 表示系ページ(データ更新なし)の場合は直接に呼び出せばよいです
    • 入力フォーム系のページは"drupal_get_form"を介して呼び出します
  • 表示系か、フォーム系かを区別せず、いつも"drupal_get_form"を介して呼び出しても構わないです
  • "drupal_get_form"がフォーム構築のプロセスが含まているので、フォームの要素構築するには必要です
    • drupa_get_form, drupal_prepare_form, drupal_retrieve_formの違い
    • これを使用する場合少し(どのぐらいは測定していないが)遅いかもしれないです
drupal
entity type
rules

Drupalのモジュール(EntityAPI)がモジュール(Rules、Viewsなど)に統合機能を提供していますが、各モジュール間の統一性が欠けています

  • 背景:Drupal7.56、EntityAPI7.x-1.8、Rules7.x-2.10
  • モジュール(EntityAPI)がほかのメジャーモジュール(Rules、Viewsなど)に統合機能を提供して、EntityAPIがよく利用されるようになります
  • ただし、各モジュール間の連携に統一性が欠けていることを気が付きました
    • 開発メンバーが違うので、開発ドキュメントが不備の場合、いろいろな誤解が生じるでしょう

例:エンティティ(Entity)へのアクセス権限管理にモジュール(Rules)が独自の権限("save")を生み出している

  • エンティティのアクセス権限チェック関数:entity_access($op, $entity_type, $entity = NULL, $account = NULL) (本家のentity_accessについての説明をご参考)
    • 引数$opは四つの権限:'view', 'update', 'create' or 'delete' (hook_entity_infoのパラメーター"access callback"を実装時にこの四つの権限を定義すればよいはずです)
  • モジュール(Rules)がエンティティアクセスチェック時に、"save"権限を生み出しています
    • rules/modules/entity_rules.incの313行あたモジュール(Rules)がエンティティに”save”権限を追加
    • 元々、モジュール(Rules)の"entity_save"は、「エンティティデータの保存」アクションです
    • ここで、"entity_save"を"save"に変換して、エンティティの"access callback"にパラメーターとして渡し、権限チェックをします
    • エンティティが定義している"access callback"に"save"権限を定義してなければ、権限がないと判断されます

対応方法1:モジュール(Rules)側でエンティティへのアクセス権限を再定義する

  • この例を限っては、モジュール(Rules)側で、エンティティへのアクセス権限の再定義します
    • Rulesの設定で「権限チェック省略」オプションがあるので、権限チェックを行わないことができます
    • Rulesの権限管理で各タイプのユーザーに権限を付与することもできます
      モジュール(Rules)でエンティティへのアクセス権限をユーザーに付与する設定

対応方法2:エンティティ側の"access callback"に"save"権限チェックを追加

  • hook_entity_info()の"access callback"の実装に、"save"権限を追加します("update"権限と同じはず)
    /**
     * Access callback for endity
     */
    function vocabulary_access($op, $entity, $account = NULL, $entity_type = NULL) {
        global $user;
    
        if (!isset($account)) {
            $account = $user;
        }
        switch ($op) {
            case 'create':
                return user_access('administer vocabulary', $account)
                || user_access('create vocabulary', $account);
            case 'view':
                return user_access('administer vocabulary', $account)
                || user_access('view vocabulary', $account);
            case 'edit':
            case 'save':    // save permission created by  entity_rule.inc
                return user_access('administer vocabulary')
                || user_access('edit any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
            case 'delete':
                return user_access('administer vocabulary')
                || user_access('delete any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
        }
    }
  • サードパーティーのモジュールでは、ソースコードの変更はしないほうがよいでしょう。対応方法1をお勧めします
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
form
menu

Drupalのhook_menu()がカスタムページ作成時によく使用されます

  • Drupalのサイトにページを追加する手段はいろいろあります
  • モジュールでページを追加し、メニューをGUIで設定することができます
    • モジュール(Views)で検索結果をページとして表示することができます(主に表示系のページ、入力フォームはなし)
    • モジュール(Panels)で各種要素を集めてページを作成します(主に表示系のページ、入力フォームなし)
    • モジュール(Web Form)でコンタクト/アンケートなどのフォームページを作成(フォーム系、入力して、データをサーバーに送信して保存)
  • プログラミングでページを作成する場合にhook_menu()を実装する必要があります
    • hook_menu()を実装時に、いろいろなパラメーター設定が紛らわしくて、注意する必要があります
    • Drupalのhook_menuでのURL設定にワイドカードの利用
    • Drupalのhook_menuでのユーザーロール別アクセス権限管理
    • 今回は、「page callback」パラメーターを詳細を説明します

Drupalのhook_menu()のパラメーター「page callback」が直接にページの呼び出しと「drupal_get_form」を介してページを呼び出す

  • ここは、ajaxで動作するチェックボックスを例として上げます
    • チェックボックスをクリックすると、ajaxでサーバーと交信する
      function ajax_add_to_my_vocabulary($form,&$form_state, $entity){
      
          global $user ;
      
          //  データがすでに存在するか否かのチェック
          $my_voca_result = db_query("SELECT entity_id FROM my_ch_vocabulary WHERE vid=".$entity->vid . " AND uid=".$user->uid)->fetchAll();
          //   ajaxコールバック時に使用するパラメーター
          $form_state['has_my_voca_result'] = count($my_voca_result)>0 ? true:false ;
          $form_state['ch_vocabulary'] = $entity ;  
      
          // ajaxのチェックボックスフォーム定義
          $form['ch_voca_form']=array(
              '#type' => 'checkbox',
              '#disabled' => $user->uid == 0 ? true : false ,
              '#title' => count($my_voca_result)>0 ? t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>') : t('<span id="ch-voca-check-op-'.$entity->vid.'">Add</span>'),
              '#default_value' => count($my_voca_result)>0 ? 1 : 0,
              '#ajax' => array(
                  'callback' => 'add_to_my_vocabulary_callback',
                  'wrapper' => 'ch-voca-check-op-'.$entity->vid,
                  'method' => 'replace',
                  'effect' => 'fade',
              ),
          );
          return $form ;
      }
      
      // ajaxのコールバック関数
      function add_to_my_vocabulary_in_views($form, $form_state){
      
          global $user;
      
          $entity = $form_state['ch_vocabulary'] ;
      
          // DBにデータがある場合に削除処理
          if( $form_state['has_my_voca_result'] ) {
              db_delete( 'my_ch_vocabulary' )->condition( 'vid', $entity->vid )->condition('uid', $user->uid )->execute();
              //  チェックボックスのラベルを「Add」に変更
              return t('<span id="ch-voca-check-op-'.$vid.'">Add</span>');
          }
      
          // データがなければ、ch_vocabuaryをmy_vocabuaryにコピー
          $my_voca = entity_create('my_ch_vocabulary', array('vid'=>$entity->vid));
          $my_voca_wrapper = entity_metadata_wrapper( 'my_ch_vocabulary', $my_voca );
          $my_voca_wrapper->vocabulary->set( $ch_voca->vocabulary ) ;
          ....   //各種フィールド、プロパティのセット
          $my_voca_wrapper->save();   // コピーされたデータの保存
      
          //  チェックボックスのラベルを「Remove」に変更
          return  t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>');
      }
  • hook_menu()が直接にajaxのチェックボックスフォームを呼び出す場合、ajaxの動作しません
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
            'title' => 'Copy and Remove',
            'page callback' => 'ajax_add_to_my_vocabulary',
            'page arguments' => array(array(), array(), 2) ,
            'access arguments' => array('create my vocabulary'),
      );
    }
    • ajaxのチェックボックスフォーム「ajax_add_to_my_vocabulary」が表示されますが、<form>要素がなくて、ajaxの動作はしません
      hook_menuが直接のフォームを呼び出す場合<form>要素が作成されずチェックボックスの動作はしない
  • hook_menu()が"drupal_get_form"を介してにajaxのチェックボックスフォームを呼び出す場合、ajaxの動作は正常です
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
       'title' => 'Copy and Remove', 
       'page callback' => 'drupal_get_form', 
       'page arguments' => array('ajax_add_to_my_vocabulary', 2) , 
       'access arguments' => array('create my vocabulary'), 
      ); 
    }
    • 作成されたフォームに"<form>"要素およびajaxの要素が含まれているのでajaxが正常に動作します
      hook_menuが"drupal_get_form"を介してフォームを呼び出す場合に"<form>"要素が含まれてajaxが正常に動作

結論:表示系ページは直接に呼び出し、入力フォーム系は"drupal_get_form"を介して呼び出す

  • hook_menu()を実装時に、呼び出すページの性質によりページの呼び出す方法を決めます
    • 表示系ページ(データ更新なし)の場合は直接に呼び出せばよいです
    • 入力フォーム系のページは"drupal_get_form"を介して呼び出します
  • 表示系か、フォーム系かを区別せず、いつも"drupal_get_form"を介して呼び出しても構わないです
  • "drupal_get_form"がフォーム構築のプロセスが含まているので、フォームの要素構築するには必要です
    • drupa_get_form, drupal_prepare_form, drupal_retrieve_formの違い
    • これを使用する場合少し(どのぐらいは測定していないが)遅いかもしれないです
drupal
entity type
rules

Drupalのモジュール(EntityAPI)がモジュール(Rules、Viewsなど)に統合機能を提供していますが、各モジュール間の統一性が欠けています

  • 背景:Drupal7.56、EntityAPI7.x-1.8、Rules7.x-2.10
  • モジュール(EntityAPI)がほかのメジャーモジュール(Rules、Viewsなど)に統合機能を提供して、EntityAPIがよく利用されるようになります
  • ただし、各モジュール間の連携に統一性が欠けていることを気が付きました
    • 開発メンバーが違うので、開発ドキュメントが不備の場合、いろいろな誤解が生じるでしょう

例:エンティティ(Entity)へのアクセス権限管理にモジュール(Rules)が独自の権限("save")を生み出している

  • エンティティのアクセス権限チェック関数:entity_access($op, $entity_type, $entity = NULL, $account = NULL) (本家のentity_accessについての説明をご参考)
    • 引数$opは四つの権限:'view', 'update', 'create' or 'delete' (hook_entity_infoのパラメーター"access callback"を実装時にこの四つの権限を定義すればよいはずです)
  • モジュール(Rules)がエンティティアクセスチェック時に、"save"権限を生み出しています
    • rules/modules/entity_rules.incの313行あたモジュール(Rules)がエンティティに”save”権限を追加
    • 元々、モジュール(Rules)の"entity_save"は、「エンティティデータの保存」アクションです
    • ここで、"entity_save"を"save"に変換して、エンティティの"access callback"にパラメーターとして渡し、権限チェックをします
    • エンティティが定義している"access callback"に"save"権限を定義してなければ、権限がないと判断されます

対応方法1:モジュール(Rules)側でエンティティへのアクセス権限を再定義する

  • この例を限っては、モジュール(Rules)側で、エンティティへのアクセス権限の再定義します
    • Rulesの設定で「権限チェック省略」オプションがあるので、権限チェックを行わないことができます
    • Rulesの権限管理で各タイプのユーザーに権限を付与することもできます
      モジュール(Rules)でエンティティへのアクセス権限をユーザーに付与する設定

対応方法2:エンティティ側の"access callback"に"save"権限チェックを追加

  • hook_entity_info()の"access callback"の実装に、"save"権限を追加します("update"権限と同じはず)
    /**
     * Access callback for endity
     */
    function vocabulary_access($op, $entity, $account = NULL, $entity_type = NULL) {
        global $user;
    
        if (!isset($account)) {
            $account = $user;
        }
        switch ($op) {
            case 'create':
                return user_access('administer vocabulary', $account)
                || user_access('create vocabulary', $account);
            case 'view':
                return user_access('administer vocabulary', $account)
                || user_access('view vocabulary', $account);
            case 'edit':
            case 'save':    // save permission created by  entity_rule.inc
                return user_access('administer vocabulary')
                || user_access('edit any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
            case 'delete':
                return user_access('administer vocabulary')
                || user_access('delete any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
        }
    }
  • サードパーティーのモジュールでは、ソースコードの変更はしないほうがよいでしょう。対応方法1をお勧めします
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
form
menu

Drupalのhook_menu()がカスタムページ作成時によく使用されます

  • Drupalのサイトにページを追加する手段はいろいろあります
  • モジュールでページを追加し、メニューをGUIで設定することができます
    • モジュール(Views)で検索結果をページとして表示することができます(主に表示系のページ、入力フォームはなし)
    • モジュール(Panels)で各種要素を集めてページを作成します(主に表示系のページ、入力フォームなし)
    • モジュール(Web Form)でコンタクト/アンケートなどのフォームページを作成(フォーム系、入力して、データをサーバーに送信して保存)
  • プログラミングでページを作成する場合にhook_menu()を実装する必要があります
    • hook_menu()を実装時に、いろいろなパラメーター設定が紛らわしくて、注意する必要があります
    • Drupalのhook_menuでのURL設定にワイドカードの利用
    • Drupalのhook_menuでのユーザーロール別アクセス権限管理
    • 今回は、「page callback」パラメーターを詳細を説明します

Drupalのhook_menu()のパラメーター「page callback」が直接にページの呼び出しと「drupal_get_form」を介してページを呼び出す

  • ここは、ajaxで動作するチェックボックスを例として上げます
    • チェックボックスをクリックすると、ajaxでサーバーと交信する
      function ajax_add_to_my_vocabulary($form,&$form_state, $entity){
      
          global $user ;
      
          //  データがすでに存在するか否かのチェック
          $my_voca_result = db_query("SELECT entity_id FROM my_ch_vocabulary WHERE vid=".$entity->vid . " AND uid=".$user->uid)->fetchAll();
          //   ajaxコールバック時に使用するパラメーター
          $form_state['has_my_voca_result'] = count($my_voca_result)>0 ? true:false ;
          $form_state['ch_vocabulary'] = $entity ;  
      
          // ajaxのチェックボックスフォーム定義
          $form['ch_voca_form']=array(
              '#type' => 'checkbox',
              '#disabled' => $user->uid == 0 ? true : false ,
              '#title' => count($my_voca_result)>0 ? t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>') : t('<span id="ch-voca-check-op-'.$entity->vid.'">Add</span>'),
              '#default_value' => count($my_voca_result)>0 ? 1 : 0,
              '#ajax' => array(
                  'callback' => 'add_to_my_vocabulary_callback',
                  'wrapper' => 'ch-voca-check-op-'.$entity->vid,
                  'method' => 'replace',
                  'effect' => 'fade',
              ),
          );
          return $form ;
      }
      
      // ajaxのコールバック関数
      function add_to_my_vocabulary_in_views($form, $form_state){
      
          global $user;
      
          $entity = $form_state['ch_vocabulary'] ;
      
          // DBにデータがある場合に削除処理
          if( $form_state['has_my_voca_result'] ) {
              db_delete( 'my_ch_vocabulary' )->condition( 'vid', $entity->vid )->condition('uid', $user->uid )->execute();
              //  チェックボックスのラベルを「Add」に変更
              return t('<span id="ch-voca-check-op-'.$vid.'">Add</span>');
          }
      
          // データがなければ、ch_vocabuaryをmy_vocabuaryにコピー
          $my_voca = entity_create('my_ch_vocabulary', array('vid'=>$entity->vid));
          $my_voca_wrapper = entity_metadata_wrapper( 'my_ch_vocabulary', $my_voca );
          $my_voca_wrapper->vocabulary->set( $ch_voca->vocabulary ) ;
          ....   //各種フィールド、プロパティのセット
          $my_voca_wrapper->save();   // コピーされたデータの保存
      
          //  チェックボックスのラベルを「Remove」に変更
          return  t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>');
      }
  • hook_menu()が直接にajaxのチェックボックスフォームを呼び出す場合、ajaxの動作しません
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
            'title' => 'Copy and Remove',
            'page callback' => 'ajax_add_to_my_vocabulary',
            'page arguments' => array(array(), array(), 2) ,
            'access arguments' => array('create my vocabulary'),
      );
    }
    • ajaxのチェックボックスフォーム「ajax_add_to_my_vocabulary」が表示されますが、<form>要素がなくて、ajaxの動作はしません
      hook_menuが直接のフォームを呼び出す場合<form>要素が作成されずチェックボックスの動作はしない
  • hook_menu()が"drupal_get_form"を介してにajaxのチェックボックスフォームを呼び出す場合、ajaxの動作は正常です
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
       'title' => 'Copy and Remove', 
       'page callback' => 'drupal_get_form', 
       'page arguments' => array('ajax_add_to_my_vocabulary', 2) , 
       'access arguments' => array('create my vocabulary'), 
      ); 
    }
    • 作成されたフォームに"<form>"要素およびajaxの要素が含まれているのでajaxが正常に動作します
      hook_menuが"drupal_get_form"を介してフォームを呼び出す場合に"<form>"要素が含まれてajaxが正常に動作

結論:表示系ページは直接に呼び出し、入力フォーム系は"drupal_get_form"を介して呼び出す

  • hook_menu()を実装時に、呼び出すページの性質によりページの呼び出す方法を決めます
    • 表示系ページ(データ更新なし)の場合は直接に呼び出せばよいです
    • 入力フォーム系のページは"drupal_get_form"を介して呼び出します
  • 表示系か、フォーム系かを区別せず、いつも"drupal_get_form"を介して呼び出しても構わないです
  • "drupal_get_form"がフォーム構築のプロセスが含まているので、フォームの要素構築するには必要です
    • drupa_get_form, drupal_prepare_form, drupal_retrieve_formの違い
    • これを使用する場合少し(どのぐらいは測定していないが)遅いかもしれないです
drupal
entity type
rules

Drupalのモジュール(EntityAPI)がモジュール(Rules、Viewsなど)に統合機能を提供していますが、各モジュール間の統一性が欠けています

  • 背景:Drupal7.56、EntityAPI7.x-1.8、Rules7.x-2.10
  • モジュール(EntityAPI)がほかのメジャーモジュール(Rules、Viewsなど)に統合機能を提供して、EntityAPIがよく利用されるようになります
  • ただし、各モジュール間の連携に統一性が欠けていることを気が付きました
    • 開発メンバーが違うので、開発ドキュメントが不備の場合、いろいろな誤解が生じるでしょう

例:エンティティ(Entity)へのアクセス権限管理にモジュール(Rules)が独自の権限("save")を生み出している

  • エンティティのアクセス権限チェック関数:entity_access($op, $entity_type, $entity = NULL, $account = NULL) (本家のentity_accessについての説明をご参考)
    • 引数$opは四つの権限:'view', 'update', 'create' or 'delete' (hook_entity_infoのパラメーター"access callback"を実装時にこの四つの権限を定義すればよいはずです)
  • モジュール(Rules)がエンティティアクセスチェック時に、"save"権限を生み出しています
    • rules/modules/entity_rules.incの313行あたモジュール(Rules)がエンティティに”save”権限を追加
    • 元々、モジュール(Rules)の"entity_save"は、「エンティティデータの保存」アクションです
    • ここで、"entity_save"を"save"に変換して、エンティティの"access callback"にパラメーターとして渡し、権限チェックをします
    • エンティティが定義している"access callback"に"save"権限を定義してなければ、権限がないと判断されます

対応方法1:モジュール(Rules)側でエンティティへのアクセス権限を再定義する

  • この例を限っては、モジュール(Rules)側で、エンティティへのアクセス権限の再定義します
    • Rulesの設定で「権限チェック省略」オプションがあるので、権限チェックを行わないことができます
    • Rulesの権限管理で各タイプのユーザーに権限を付与することもできます
      モジュール(Rules)でエンティティへのアクセス権限をユーザーに付与する設定

対応方法2:エンティティ側の"access callback"に"save"権限チェックを追加

  • hook_entity_info()の"access callback"の実装に、"save"権限を追加します("update"権限と同じはず)
    /**
     * Access callback for endity
     */
    function vocabulary_access($op, $entity, $account = NULL, $entity_type = NULL) {
        global $user;
    
        if (!isset($account)) {
            $account = $user;
        }
        switch ($op) {
            case 'create':
                return user_access('administer vocabulary', $account)
                || user_access('create vocabulary', $account);
            case 'view':
                return user_access('administer vocabulary', $account)
                || user_access('view vocabulary', $account);
            case 'edit':
            case 'save':    // save permission created by  entity_rule.inc
                return user_access('administer vocabulary')
                || user_access('edit any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
            case 'delete':
                return user_access('administer vocabulary')
                || user_access('delete any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
        }
    }
  • サードパーティーのモジュールでは、ソースコードの変更はしないほうがよいでしょう。対応方法1をお勧めします
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
form
menu

Drupalのhook_menu()がカスタムページ作成時によく使用されます

  • Drupalのサイトにページを追加する手段はいろいろあります
  • モジュールでページを追加し、メニューをGUIで設定することができます
    • モジュール(Views)で検索結果をページとして表示することができます(主に表示系のページ、入力フォームはなし)
    • モジュール(Panels)で各種要素を集めてページを作成します(主に表示系のページ、入力フォームなし)
    • モジュール(Web Form)でコンタクト/アンケートなどのフォームページを作成(フォーム系、入力して、データをサーバーに送信して保存)
  • プログラミングでページを作成する場合にhook_menu()を実装する必要があります
    • hook_menu()を実装時に、いろいろなパラメーター設定が紛らわしくて、注意する必要があります
    • Drupalのhook_menuでのURL設定にワイドカードの利用
    • Drupalのhook_menuでのユーザーロール別アクセス権限管理
    • 今回は、「page callback」パラメーターを詳細を説明します

Drupalのhook_menu()のパラメーター「page callback」が直接にページの呼び出しと「drupal_get_form」を介してページを呼び出す

  • ここは、ajaxで動作するチェックボックスを例として上げます
    • チェックボックスをクリックすると、ajaxでサーバーと交信する
      function ajax_add_to_my_vocabulary($form,&$form_state, $entity){
      
          global $user ;
      
          //  データがすでに存在するか否かのチェック
          $my_voca_result = db_query("SELECT entity_id FROM my_ch_vocabulary WHERE vid=".$entity->vid . " AND uid=".$user->uid)->fetchAll();
          //   ajaxコールバック時に使用するパラメーター
          $form_state['has_my_voca_result'] = count($my_voca_result)>0 ? true:false ;
          $form_state['ch_vocabulary'] = $entity ;  
      
          // ajaxのチェックボックスフォーム定義
          $form['ch_voca_form']=array(
              '#type' => 'checkbox',
              '#disabled' => $user->uid == 0 ? true : false ,
              '#title' => count($my_voca_result)>0 ? t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>') : t('<span id="ch-voca-check-op-'.$entity->vid.'">Add</span>'),
              '#default_value' => count($my_voca_result)>0 ? 1 : 0,
              '#ajax' => array(
                  'callback' => 'add_to_my_vocabulary_callback',
                  'wrapper' => 'ch-voca-check-op-'.$entity->vid,
                  'method' => 'replace',
                  'effect' => 'fade',
              ),
          );
          return $form ;
      }
      
      // ajaxのコールバック関数
      function add_to_my_vocabulary_in_views($form, $form_state){
      
          global $user;
      
          $entity = $form_state['ch_vocabulary'] ;
      
          // DBにデータがある場合に削除処理
          if( $form_state['has_my_voca_result'] ) {
              db_delete( 'my_ch_vocabulary' )->condition( 'vid', $entity->vid )->condition('uid', $user->uid )->execute();
              //  チェックボックスのラベルを「Add」に変更
              return t('<span id="ch-voca-check-op-'.$vid.'">Add</span>');
          }
      
          // データがなければ、ch_vocabuaryをmy_vocabuaryにコピー
          $my_voca = entity_create('my_ch_vocabulary', array('vid'=>$entity->vid));
          $my_voca_wrapper = entity_metadata_wrapper( 'my_ch_vocabulary', $my_voca );
          $my_voca_wrapper->vocabulary->set( $ch_voca->vocabulary ) ;
          ....   //各種フィールド、プロパティのセット
          $my_voca_wrapper->save();   // コピーされたデータの保存
      
          //  チェックボックスのラベルを「Remove」に変更
          return  t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>');
      }
  • hook_menu()が直接にajaxのチェックボックスフォームを呼び出す場合、ajaxの動作しません
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
            'title' => 'Copy and Remove',
            'page callback' => 'ajax_add_to_my_vocabulary',
            'page arguments' => array(array(), array(), 2) ,
            'access arguments' => array('create my vocabulary'),
      );
    }
    • ajaxのチェックボックスフォーム「ajax_add_to_my_vocabulary」が表示されますが、<form>要素がなくて、ajaxの動作はしません
      hook_menuが直接のフォームを呼び出す場合<form>要素が作成されずチェックボックスの動作はしない
  • hook_menu()が"drupal_get_form"を介してにajaxのチェックボックスフォームを呼び出す場合、ajaxの動作は正常です
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
       'title' => 'Copy and Remove', 
       'page callback' => 'drupal_get_form', 
       'page arguments' => array('ajax_add_to_my_vocabulary', 2) , 
       'access arguments' => array('create my vocabulary'), 
      ); 
    }
    • 作成されたフォームに"<form>"要素およびajaxの要素が含まれているのでajaxが正常に動作します
      hook_menuが"drupal_get_form"を介してフォームを呼び出す場合に"<form>"要素が含まれてajaxが正常に動作

結論:表示系ページは直接に呼び出し、入力フォーム系は"drupal_get_form"を介して呼び出す

  • hook_menu()を実装時に、呼び出すページの性質によりページの呼び出す方法を決めます
    • 表示系ページ(データ更新なし)の場合は直接に呼び出せばよいです
    • 入力フォーム系のページは"drupal_get_form"を介して呼び出します
  • 表示系か、フォーム系かを区別せず、いつも"drupal_get_form"を介して呼び出しても構わないです
  • "drupal_get_form"がフォーム構築のプロセスが含まているので、フォームの要素構築するには必要です
    • drupa_get_form, drupal_prepare_form, drupal_retrieve_formの違い
    • これを使用する場合少し(どのぐらいは測定していないが)遅いかもしれないです
drupal
entity type
rules

Drupalのモジュール(EntityAPI)がモジュール(Rules、Viewsなど)に統合機能を提供していますが、各モジュール間の統一性が欠けています

  • 背景:Drupal7.56、EntityAPI7.x-1.8、Rules7.x-2.10
  • モジュール(EntityAPI)がほかのメジャーモジュール(Rules、Viewsなど)に統合機能を提供して、EntityAPIがよく利用されるようになります
  • ただし、各モジュール間の連携に統一性が欠けていることを気が付きました
    • 開発メンバーが違うので、開発ドキュメントが不備の場合、いろいろな誤解が生じるでしょう

例:エンティティ(Entity)へのアクセス権限管理にモジュール(Rules)が独自の権限("save")を生み出している

  • エンティティのアクセス権限チェック関数:entity_access($op, $entity_type, $entity = NULL, $account = NULL) (本家のentity_accessについての説明をご参考)
    • 引数$opは四つの権限:'view', 'update', 'create' or 'delete' (hook_entity_infoのパラメーター"access callback"を実装時にこの四つの権限を定義すればよいはずです)
  • モジュール(Rules)がエンティティアクセスチェック時に、"save"権限を生み出しています
    • rules/modules/entity_rules.incの313行あたモジュール(Rules)がエンティティに”save”権限を追加
    • 元々、モジュール(Rules)の"entity_save"は、「エンティティデータの保存」アクションです
    • ここで、"entity_save"を"save"に変換して、エンティティの"access callback"にパラメーターとして渡し、権限チェックをします
    • エンティティが定義している"access callback"に"save"権限を定義してなければ、権限がないと判断されます

対応方法1:モジュール(Rules)側でエンティティへのアクセス権限を再定義する

  • この例を限っては、モジュール(Rules)側で、エンティティへのアクセス権限の再定義します
    • Rulesの設定で「権限チェック省略」オプションがあるので、権限チェックを行わないことができます
    • Rulesの権限管理で各タイプのユーザーに権限を付与することもできます
      モジュール(Rules)でエンティティへのアクセス権限をユーザーに付与する設定

対応方法2:エンティティ側の"access callback"に"save"権限チェックを追加

  • hook_entity_info()の"access callback"の実装に、"save"権限を追加します("update"権限と同じはず)
    /**
     * Access callback for endity
     */
    function vocabulary_access($op, $entity, $account = NULL, $entity_type = NULL) {
        global $user;
    
        if (!isset($account)) {
            $account = $user;
        }
        switch ($op) {
            case 'create':
                return user_access('administer vocabulary', $account)
                || user_access('create vocabulary', $account);
            case 'view':
                return user_access('administer vocabulary', $account)
                || user_access('view vocabulary', $account);
            case 'edit':
            case 'save':    // save permission created by  entity_rule.inc
                return user_access('administer vocabulary')
                || user_access('edit any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
            case 'delete':
                return user_access('administer vocabulary')
                || user_access('delete any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
        }
    }
  • サードパーティーのモジュールでは、ソースコードの変更はしないほうがよいでしょう。対応方法1をお勧めします
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
form
menu

Drupalのhook_menu()がカスタムページ作成時によく使用されます

  • Drupalのサイトにページを追加する手段はいろいろあります
  • モジュールでページを追加し、メニューをGUIで設定することができます
    • モジュール(Views)で検索結果をページとして表示することができます(主に表示系のページ、入力フォームはなし)
    • モジュール(Panels)で各種要素を集めてページを作成します(主に表示系のページ、入力フォームなし)
    • モジュール(Web Form)でコンタクト/アンケートなどのフォームページを作成(フォーム系、入力して、データをサーバーに送信して保存)
  • プログラミングでページを作成する場合にhook_menu()を実装する必要があります
    • hook_menu()を実装時に、いろいろなパラメーター設定が紛らわしくて、注意する必要があります
    • Drupalのhook_menuでのURL設定にワイドカードの利用
    • Drupalのhook_menuでのユーザーロール別アクセス権限管理
    • 今回は、「page callback」パラメーターを詳細を説明します

Drupalのhook_menu()のパラメーター「page callback」が直接にページの呼び出しと「drupal_get_form」を介してページを呼び出す

  • ここは、ajaxで動作するチェックボックスを例として上げます
    • チェックボックスをクリックすると、ajaxでサーバーと交信する
      function ajax_add_to_my_vocabulary($form,&$form_state, $entity){
      
          global $user ;
      
          //  データがすでに存在するか否かのチェック
          $my_voca_result = db_query("SELECT entity_id FROM my_ch_vocabulary WHERE vid=".$entity->vid . " AND uid=".$user->uid)->fetchAll();
          //   ajaxコールバック時に使用するパラメーター
          $form_state['has_my_voca_result'] = count($my_voca_result)>0 ? true:false ;
          $form_state['ch_vocabulary'] = $entity ;  
      
          // ajaxのチェックボックスフォーム定義
          $form['ch_voca_form']=array(
              '#type' => 'checkbox',
              '#disabled' => $user->uid == 0 ? true : false ,
              '#title' => count($my_voca_result)>0 ? t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>') : t('<span id="ch-voca-check-op-'.$entity->vid.'">Add</span>'),
              '#default_value' => count($my_voca_result)>0 ? 1 : 0,
              '#ajax' => array(
                  'callback' => 'add_to_my_vocabulary_callback',
                  'wrapper' => 'ch-voca-check-op-'.$entity->vid,
                  'method' => 'replace',
                  'effect' => 'fade',
              ),
          );
          return $form ;
      }
      
      // ajaxのコールバック関数
      function add_to_my_vocabulary_in_views($form, $form_state){
      
          global $user;
      
          $entity = $form_state['ch_vocabulary'] ;
      
          // DBにデータがある場合に削除処理
          if( $form_state['has_my_voca_result'] ) {
              db_delete( 'my_ch_vocabulary' )->condition( 'vid', $entity->vid )->condition('uid', $user->uid )->execute();
              //  チェックボックスのラベルを「Add」に変更
              return t('<span id="ch-voca-check-op-'.$vid.'">Add</span>');
          }
      
          // データがなければ、ch_vocabuaryをmy_vocabuaryにコピー
          $my_voca = entity_create('my_ch_vocabulary', array('vid'=>$entity->vid));
          $my_voca_wrapper = entity_metadata_wrapper( 'my_ch_vocabulary', $my_voca );
          $my_voca_wrapper->vocabulary->set( $ch_voca->vocabulary ) ;
          ....   //各種フィールド、プロパティのセット
          $my_voca_wrapper->save();   // コピーされたデータの保存
      
          //  チェックボックスのラベルを「Remove」に変更
          return  t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>');
      }
  • hook_menu()が直接にajaxのチェックボックスフォームを呼び出す場合、ajaxの動作しません
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
            'title' => 'Copy and Remove',
            'page callback' => 'ajax_add_to_my_vocabulary',
            'page arguments' => array(array(), array(), 2) ,
            'access arguments' => array('create my vocabulary'),
      );
    }
    • ajaxのチェックボックスフォーム「ajax_add_to_my_vocabulary」が表示されますが、<form>要素がなくて、ajaxの動作はしません
      hook_menuが直接のフォームを呼び出す場合<form>要素が作成されずチェックボックスの動作はしない
  • hook_menu()が"drupal_get_form"を介してにajaxのチェックボックスフォームを呼び出す場合、ajaxの動作は正常です
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
       'title' => 'Copy and Remove', 
       'page callback' => 'drupal_get_form', 
       'page arguments' => array('ajax_add_to_my_vocabulary', 2) , 
       'access arguments' => array('create my vocabulary'), 
      ); 
    }
    • 作成されたフォームに"<form>"要素およびajaxの要素が含まれているのでajaxが正常に動作します
      hook_menuが"drupal_get_form"を介してフォームを呼び出す場合に"<form>"要素が含まれてajaxが正常に動作

結論:表示系ページは直接に呼び出し、入力フォーム系は"drupal_get_form"を介して呼び出す

  • hook_menu()を実装時に、呼び出すページの性質によりページの呼び出す方法を決めます
    • 表示系ページ(データ更新なし)の場合は直接に呼び出せばよいです
    • 入力フォーム系のページは"drupal_get_form"を介して呼び出します
  • 表示系か、フォーム系かを区別せず、いつも"drupal_get_form"を介して呼び出しても構わないです
  • "drupal_get_form"がフォーム構築のプロセスが含まているので、フォームの要素構築するには必要です
    • drupa_get_form, drupal_prepare_form, drupal_retrieve_formの違い
    • これを使用する場合少し(どのぐらいは測定していないが)遅いかもしれないです
drupal
entity type
rules

Drupalのモジュール(EntityAPI)がモジュール(Rules、Viewsなど)に統合機能を提供していますが、各モジュール間の統一性が欠けています

  • 背景:Drupal7.56、EntityAPI7.x-1.8、Rules7.x-2.10
  • モジュール(EntityAPI)がほかのメジャーモジュール(Rules、Viewsなど)に統合機能を提供して、EntityAPIがよく利用されるようになります
  • ただし、各モジュール間の連携に統一性が欠けていることを気が付きました
    • 開発メンバーが違うので、開発ドキュメントが不備の場合、いろいろな誤解が生じるでしょう

例:エンティティ(Entity)へのアクセス権限管理にモジュール(Rules)が独自の権限("save")を生み出している

  • エンティティのアクセス権限チェック関数:entity_access($op, $entity_type, $entity = NULL, $account = NULL) (本家のentity_accessについての説明をご参考)
    • 引数$opは四つの権限:'view', 'update', 'create' or 'delete' (hook_entity_infoのパラメーター"access callback"を実装時にこの四つの権限を定義すればよいはずです)
  • モジュール(Rules)がエンティティアクセスチェック時に、"save"権限を生み出しています
    • rules/modules/entity_rules.incの313行あたモジュール(Rules)がエンティティに”save”権限を追加
    • 元々、モジュール(Rules)の"entity_save"は、「エンティティデータの保存」アクションです
    • ここで、"entity_save"を"save"に変換して、エンティティの"access callback"にパラメーターとして渡し、権限チェックをします
    • エンティティが定義している"access callback"に"save"権限を定義してなければ、権限がないと判断されます

対応方法1:モジュール(Rules)側でエンティティへのアクセス権限を再定義する

  • この例を限っては、モジュール(Rules)側で、エンティティへのアクセス権限の再定義します
    • Rulesの設定で「権限チェック省略」オプションがあるので、権限チェックを行わないことができます
    • Rulesの権限管理で各タイプのユーザーに権限を付与することもできます
      モジュール(Rules)でエンティティへのアクセス権限をユーザーに付与する設定

対応方法2:エンティティ側の"access callback"に"save"権限チェックを追加

  • hook_entity_info()の"access callback"の実装に、"save"権限を追加します("update"権限と同じはず)
    /**
     * Access callback for endity
     */
    function vocabulary_access($op, $entity, $account = NULL, $entity_type = NULL) {
        global $user;
    
        if (!isset($account)) {
            $account = $user;
        }
        switch ($op) {
            case 'create':
                return user_access('administer vocabulary', $account)
                || user_access('create vocabulary', $account);
            case 'view':
                return user_access('administer vocabulary', $account)
                || user_access('view vocabulary', $account);
            case 'edit':
            case 'save':    // save permission created by  entity_rule.inc
                return user_access('administer vocabulary')
                || user_access('edit any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
            case 'delete':
                return user_access('administer vocabulary')
                || user_access('delete any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
        }
    }
  • サードパーティーのモジュールでは、ソースコードの変更はしないほうがよいでしょう。対応方法1をお勧めします
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
form
menu

Drupalのhook_menu()がカスタムページ作成時によく使用されます

  • Drupalのサイトにページを追加する手段はいろいろあります
  • モジュールでページを追加し、メニューをGUIで設定することができます
    • モジュール(Views)で検索結果をページとして表示することができます(主に表示系のページ、入力フォームはなし)
    • モジュール(Panels)で各種要素を集めてページを作成します(主に表示系のページ、入力フォームなし)
    • モジュール(Web Form)でコンタクト/アンケートなどのフォームページを作成(フォーム系、入力して、データをサーバーに送信して保存)
  • プログラミングでページを作成する場合にhook_menu()を実装する必要があります
    • hook_menu()を実装時に、いろいろなパラメーター設定が紛らわしくて、注意する必要があります
    • Drupalのhook_menuでのURL設定にワイドカードの利用
    • Drupalのhook_menuでのユーザーロール別アクセス権限管理
    • 今回は、「page callback」パラメーターを詳細を説明します

Drupalのhook_menu()のパラメーター「page callback」が直接にページの呼び出しと「drupal_get_form」を介してページを呼び出す

  • ここは、ajaxで動作するチェックボックスを例として上げます
    • チェックボックスをクリックすると、ajaxでサーバーと交信する
      function ajax_add_to_my_vocabulary($form,&$form_state, $entity){
      
          global $user ;
      
          //  データがすでに存在するか否かのチェック
          $my_voca_result = db_query("SELECT entity_id FROM my_ch_vocabulary WHERE vid=".$entity->vid . " AND uid=".$user->uid)->fetchAll();
          //   ajaxコールバック時に使用するパラメーター
          $form_state['has_my_voca_result'] = count($my_voca_result)>0 ? true:false ;
          $form_state['ch_vocabulary'] = $entity ;  
      
          // ajaxのチェックボックスフォーム定義
          $form['ch_voca_form']=array(
              '#type' => 'checkbox',
              '#disabled' => $user->uid == 0 ? true : false ,
              '#title' => count($my_voca_result)>0 ? t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>') : t('<span id="ch-voca-check-op-'.$entity->vid.'">Add</span>'),
              '#default_value' => count($my_voca_result)>0 ? 1 : 0,
              '#ajax' => array(
                  'callback' => 'add_to_my_vocabulary_callback',
                  'wrapper' => 'ch-voca-check-op-'.$entity->vid,
                  'method' => 'replace',
                  'effect' => 'fade',
              ),
          );
          return $form ;
      }
      
      // ajaxのコールバック関数
      function add_to_my_vocabulary_in_views($form, $form_state){
      
          global $user;
      
          $entity = $form_state['ch_vocabulary'] ;
      
          // DBにデータがある場合に削除処理
          if( $form_state['has_my_voca_result'] ) {
              db_delete( 'my_ch_vocabulary' )->condition( 'vid', $entity->vid )->condition('uid', $user->uid )->execute();
              //  チェックボックスのラベルを「Add」に変更
              return t('<span id="ch-voca-check-op-'.$vid.'">Add</span>');
          }
      
          // データがなければ、ch_vocabuaryをmy_vocabuaryにコピー
          $my_voca = entity_create('my_ch_vocabulary', array('vid'=>$entity->vid));
          $my_voca_wrapper = entity_metadata_wrapper( 'my_ch_vocabulary', $my_voca );
          $my_voca_wrapper->vocabulary->set( $ch_voca->vocabulary ) ;
          ....   //各種フィールド、プロパティのセット
          $my_voca_wrapper->save();   // コピーされたデータの保存
      
          //  チェックボックスのラベルを「Remove」に変更
          return  t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>');
      }
  • hook_menu()が直接にajaxのチェックボックスフォームを呼び出す場合、ajaxの動作しません
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
            'title' => 'Copy and Remove',
            'page callback' => 'ajax_add_to_my_vocabulary',
            'page arguments' => array(array(), array(), 2) ,
            'access arguments' => array('create my vocabulary'),
      );
    }
    • ajaxのチェックボックスフォーム「ajax_add_to_my_vocabulary」が表示されますが、<form>要素がなくて、ajaxの動作はしません
      hook_menuが直接のフォームを呼び出す場合<form>要素が作成されずチェックボックスの動作はしない
  • hook_menu()が"drupal_get_form"を介してにajaxのチェックボックスフォームを呼び出す場合、ajaxの動作は正常です
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
       'title' => 'Copy and Remove', 
       'page callback' => 'drupal_get_form', 
       'page arguments' => array('ajax_add_to_my_vocabulary', 2) , 
       'access arguments' => array('create my vocabulary'), 
      ); 
    }
    • 作成されたフォームに"<form>"要素およびajaxの要素が含まれているのでajaxが正常に動作します
      hook_menuが"drupal_get_form"を介してフォームを呼び出す場合に"<form>"要素が含まれてajaxが正常に動作

結論:表示系ページは直接に呼び出し、入力フォーム系は"drupal_get_form"を介して呼び出す

  • hook_menu()を実装時に、呼び出すページの性質によりページの呼び出す方法を決めます
    • 表示系ページ(データ更新なし)の場合は直接に呼び出せばよいです
    • 入力フォーム系のページは"drupal_get_form"を介して呼び出します
  • 表示系か、フォーム系かを区別せず、いつも"drupal_get_form"を介して呼び出しても構わないです
  • "drupal_get_form"がフォーム構築のプロセスが含まているので、フォームの要素構築するには必要です
    • drupa_get_form, drupal_prepare_form, drupal_retrieve_formの違い
    • これを使用する場合少し(どのぐらいは測定していないが)遅いかもしれないです
drupal
entity type
rules

Drupalのモジュール(EntityAPI)がモジュール(Rules、Viewsなど)に統合機能を提供していますが、各モジュール間の統一性が欠けています

  • 背景:Drupal7.56、EntityAPI7.x-1.8、Rules7.x-2.10
  • モジュール(EntityAPI)がほかのメジャーモジュール(Rules、Viewsなど)に統合機能を提供して、EntityAPIがよく利用されるようになります
  • ただし、各モジュール間の連携に統一性が欠けていることを気が付きました
    • 開発メンバーが違うので、開発ドキュメントが不備の場合、いろいろな誤解が生じるでしょう

例:エンティティ(Entity)へのアクセス権限管理にモジュール(Rules)が独自の権限("save")を生み出している

  • エンティティのアクセス権限チェック関数:entity_access($op, $entity_type, $entity = NULL, $account = NULL) (本家のentity_accessについての説明をご参考)
    • 引数$opは四つの権限:'view', 'update', 'create' or 'delete' (hook_entity_infoのパラメーター"access callback"を実装時にこの四つの権限を定義すればよいはずです)
  • モジュール(Rules)がエンティティアクセスチェック時に、"save"権限を生み出しています
    • rules/modules/entity_rules.incの313行あたモジュール(Rules)がエンティティに”save”権限を追加
    • 元々、モジュール(Rules)の"entity_save"は、「エンティティデータの保存」アクションです
    • ここで、"entity_save"を"save"に変換して、エンティティの"access callback"にパラメーターとして渡し、権限チェックをします
    • エンティティが定義している"access callback"に"save"権限を定義してなければ、権限がないと判断されます

対応方法1:モジュール(Rules)側でエンティティへのアクセス権限を再定義する

  • この例を限っては、モジュール(Rules)側で、エンティティへのアクセス権限の再定義します
    • Rulesの設定で「権限チェック省略」オプションがあるので、権限チェックを行わないことができます
    • Rulesの権限管理で各タイプのユーザーに権限を付与することもできます
      モジュール(Rules)でエンティティへのアクセス権限をユーザーに付与する設定

対応方法2:エンティティ側の"access callback"に"save"権限チェックを追加

  • hook_entity_info()の"access callback"の実装に、"save"権限を追加します("update"権限と同じはず)
    /**
     * Access callback for endity
     */
    function vocabulary_access($op, $entity, $account = NULL, $entity_type = NULL) {
        global $user;
    
        if (!isset($account)) {
            $account = $user;
        }
        switch ($op) {
            case 'create':
                return user_access('administer vocabulary', $account)
                || user_access('create vocabulary', $account);
            case 'view':
                return user_access('administer vocabulary', $account)
                || user_access('view vocabulary', $account);
            case 'edit':
            case 'save':    // save permission created by  entity_rule.inc
                return user_access('administer vocabulary')
                || user_access('edit any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
            case 'delete':
                return user_access('administer vocabulary')
                || user_access('delete any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
        }
    }
  • サードパーティーのモジュールでは、ソースコードの変更はしないほうがよいでしょう。対応方法1をお勧めします
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
form
menu

Drupalのhook_menu()がカスタムページ作成時によく使用されます

  • Drupalのサイトにページを追加する手段はいろいろあります
  • モジュールでページを追加し、メニューをGUIで設定することができます
    • モジュール(Views)で検索結果をページとして表示することができます(主に表示系のページ、入力フォームはなし)
    • モジュール(Panels)で各種要素を集めてページを作成します(主に表示系のページ、入力フォームなし)
    • モジュール(Web Form)でコンタクト/アンケートなどのフォームページを作成(フォーム系、入力して、データをサーバーに送信して保存)
  • プログラミングでページを作成する場合にhook_menu()を実装する必要があります
    • hook_menu()を実装時に、いろいろなパラメーター設定が紛らわしくて、注意する必要があります
    • Drupalのhook_menuでのURL設定にワイドカードの利用
    • Drupalのhook_menuでのユーザーロール別アクセス権限管理
    • 今回は、「page callback」パラメーターを詳細を説明します

Drupalのhook_menu()のパラメーター「page callback」が直接にページの呼び出しと「drupal_get_form」を介してページを呼び出す

  • ここは、ajaxで動作するチェックボックスを例として上げます
    • チェックボックスをクリックすると、ajaxでサーバーと交信する
      function ajax_add_to_my_vocabulary($form,&$form_state, $entity){
      
          global $user ;
      
          //  データがすでに存在するか否かのチェック
          $my_voca_result = db_query("SELECT entity_id FROM my_ch_vocabulary WHERE vid=".$entity->vid . " AND uid=".$user->uid)->fetchAll();
          //   ajaxコールバック時に使用するパラメーター
          $form_state['has_my_voca_result'] = count($my_voca_result)>0 ? true:false ;
          $form_state['ch_vocabulary'] = $entity ;  
      
          // ajaxのチェックボックスフォーム定義
          $form['ch_voca_form']=array(
              '#type' => 'checkbox',
              '#disabled' => $user->uid == 0 ? true : false ,
              '#title' => count($my_voca_result)>0 ? t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>') : t('<span id="ch-voca-check-op-'.$entity->vid.'">Add</span>'),
              '#default_value' => count($my_voca_result)>0 ? 1 : 0,
              '#ajax' => array(
                  'callback' => 'add_to_my_vocabulary_callback',
                  'wrapper' => 'ch-voca-check-op-'.$entity->vid,
                  'method' => 'replace',
                  'effect' => 'fade',
              ),
          );
          return $form ;
      }
      
      // ajaxのコールバック関数
      function add_to_my_vocabulary_in_views($form, $form_state){
      
          global $user;
      
          $entity = $form_state['ch_vocabulary'] ;
      
          // DBにデータがある場合に削除処理
          if( $form_state['has_my_voca_result'] ) {
              db_delete( 'my_ch_vocabulary' )->condition( 'vid', $entity->vid )->condition('uid', $user->uid )->execute();
              //  チェックボックスのラベルを「Add」に変更
              return t('<span id="ch-voca-check-op-'.$vid.'">Add</span>');
          }
      
          // データがなければ、ch_vocabuaryをmy_vocabuaryにコピー
          $my_voca = entity_create('my_ch_vocabulary', array('vid'=>$entity->vid));
          $my_voca_wrapper = entity_metadata_wrapper( 'my_ch_vocabulary', $my_voca );
          $my_voca_wrapper->vocabulary->set( $ch_voca->vocabulary ) ;
          ....   //各種フィールド、プロパティのセット
          $my_voca_wrapper->save();   // コピーされたデータの保存
      
          //  チェックボックスのラベルを「Remove」に変更
          return  t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>');
      }
  • hook_menu()が直接にajaxのチェックボックスフォームを呼び出す場合、ajaxの動作しません
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
            'title' => 'Copy and Remove',
            'page callback' => 'ajax_add_to_my_vocabulary',
            'page arguments' => array(array(), array(), 2) ,
            'access arguments' => array('create my vocabulary'),
      );
    }
    • ajaxのチェックボックスフォーム「ajax_add_to_my_vocabulary」が表示されますが、<form>要素がなくて、ajaxの動作はしません
      hook_menuが直接のフォームを呼び出す場合<form>要素が作成されずチェックボックスの動作はしない
  • hook_menu()が"drupal_get_form"を介してにajaxのチェックボックスフォームを呼び出す場合、ajaxの動作は正常です
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
       'title' => 'Copy and Remove', 
       'page callback' => 'drupal_get_form', 
       'page arguments' => array('ajax_add_to_my_vocabulary', 2) , 
       'access arguments' => array('create my vocabulary'), 
      ); 
    }
    • 作成されたフォームに"<form>"要素およびajaxの要素が含まれているのでajaxが正常に動作します
      hook_menuが"drupal_get_form"を介してフォームを呼び出す場合に"<form>"要素が含まれてajaxが正常に動作

結論:表示系ページは直接に呼び出し、入力フォーム系は"drupal_get_form"を介して呼び出す

  • hook_menu()を実装時に、呼び出すページの性質によりページの呼び出す方法を決めます
    • 表示系ページ(データ更新なし)の場合は直接に呼び出せばよいです
    • 入力フォーム系のページは"drupal_get_form"を介して呼び出します
  • 表示系か、フォーム系かを区別せず、いつも"drupal_get_form"を介して呼び出しても構わないです
  • "drupal_get_form"がフォーム構築のプロセスが含まているので、フォームの要素構築するには必要です
    • drupa_get_form, drupal_prepare_form, drupal_retrieve_formの違い
    • これを使用する場合少し(どのぐらいは測定していないが)遅いかもしれないです
drupal
entity type
rules

Drupalのモジュール(EntityAPI)がモジュール(Rules、Viewsなど)に統合機能を提供していますが、各モジュール間の統一性が欠けています

  • 背景:Drupal7.56、EntityAPI7.x-1.8、Rules7.x-2.10
  • モジュール(EntityAPI)がほかのメジャーモジュール(Rules、Viewsなど)に統合機能を提供して、EntityAPIがよく利用されるようになります
  • ただし、各モジュール間の連携に統一性が欠けていることを気が付きました
    • 開発メンバーが違うので、開発ドキュメントが不備の場合、いろいろな誤解が生じるでしょう

例:エンティティ(Entity)へのアクセス権限管理にモジュール(Rules)が独自の権限("save")を生み出している

  • エンティティのアクセス権限チェック関数:entity_access($op, $entity_type, $entity = NULL, $account = NULL) (本家のentity_accessについての説明をご参考)
    • 引数$opは四つの権限:'view', 'update', 'create' or 'delete' (hook_entity_infoのパラメーター"access callback"を実装時にこの四つの権限を定義すればよいはずです)
  • モジュール(Rules)がエンティティアクセスチェック時に、"save"権限を生み出しています
    • rules/modules/entity_rules.incの313行あたモジュール(Rules)がエンティティに”save”権限を追加
    • 元々、モジュール(Rules)の"entity_save"は、「エンティティデータの保存」アクションです
    • ここで、"entity_save"を"save"に変換して、エンティティの"access callback"にパラメーターとして渡し、権限チェックをします
    • エンティティが定義している"access callback"に"save"権限を定義してなければ、権限がないと判断されます

対応方法1:モジュール(Rules)側でエンティティへのアクセス権限を再定義する

  • この例を限っては、モジュール(Rules)側で、エンティティへのアクセス権限の再定義します
    • Rulesの設定で「権限チェック省略」オプションがあるので、権限チェックを行わないことができます
    • Rulesの権限管理で各タイプのユーザーに権限を付与することもできます
      モジュール(Rules)でエンティティへのアクセス権限をユーザーに付与する設定

対応方法2:エンティティ側の"access callback"に"save"権限チェックを追加

  • hook_entity_info()の"access callback"の実装に、"save"権限を追加します("update"権限と同じはず)
    /**
     * Access callback for endity
     */
    function vocabulary_access($op, $entity, $account = NULL, $entity_type = NULL) {
        global $user;
    
        if (!isset($account)) {
            $account = $user;
        }
        switch ($op) {
            case 'create':
                return user_access('administer vocabulary', $account)
                || user_access('create vocabulary', $account);
            case 'view':
                return user_access('administer vocabulary', $account)
                || user_access('view vocabulary', $account);
            case 'edit':
            case 'save':    // save permission created by  entity_rule.inc
                return user_access('administer vocabulary')
                || user_access('edit any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
            case 'delete':
                return user_access('administer vocabulary')
                || user_access('delete any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
        }
    }
  • サードパーティーのモジュールでは、ソースコードの変更はしないほうがよいでしょう。対応方法1をお勧めします
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
drupal
form
menu

Drupalのhook_menu()がカスタムページ作成時によく使用されます

  • Drupalのサイトにページを追加する手段はいろいろあります
  • モジュールでページを追加し、メニューをGUIで設定することができます
    • モジュール(Views)で検索結果をページとして表示することができます(主に表示系のページ、入力フォームはなし)
    • モジュール(Panels)で各種要素を集めてページを作成します(主に表示系のページ、入力フォームなし)
    • モジュール(Web Form)でコンタクト/アンケートなどのフォームページを作成(フォーム系、入力して、データをサーバーに送信して保存)
  • プログラミングでページを作成する場合にhook_menu()を実装する必要があります
    • hook_menu()を実装時に、いろいろなパラメーター設定が紛らわしくて、注意する必要があります
    • Drupalのhook_menuでのURL設定にワイドカードの利用
    • Drupalのhook_menuでのユーザーロール別アクセス権限管理
    • 今回は、「page callback」パラメーターを詳細を説明します

Drupalのhook_menu()のパラメーター「page callback」が直接にページの呼び出しと「drupal_get_form」を介してページを呼び出す

  • ここは、ajaxで動作するチェックボックスを例として上げます
    • チェックボックスをクリックすると、ajaxでサーバーと交信する
      function ajax_add_to_my_vocabulary($form,&$form_state, $entity){
      
          global $user ;
      
          //  データがすでに存在するか否かのチェック
          $my_voca_result = db_query("SELECT entity_id FROM my_ch_vocabulary WHERE vid=".$entity->vid . " AND uid=".$user->uid)->fetchAll();
          //   ajaxコールバック時に使用するパラメーター
          $form_state['has_my_voca_result'] = count($my_voca_result)>0 ? true:false ;
          $form_state['ch_vocabulary'] = $entity ;  
      
          // ajaxのチェックボックスフォーム定義
          $form['ch_voca_form']=array(
              '#type' => 'checkbox',
              '#disabled' => $user->uid == 0 ? true : false ,
              '#title' => count($my_voca_result)>0 ? t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>') : t('<span id="ch-voca-check-op-'.$entity->vid.'">Add</span>'),
              '#default_value' => count($my_voca_result)>0 ? 1 : 0,
              '#ajax' => array(
                  'callback' => 'add_to_my_vocabulary_callback',
                  'wrapper' => 'ch-voca-check-op-'.$entity->vid,
                  'method' => 'replace',
                  'effect' => 'fade',
              ),
          );
          return $form ;
      }
      
      // ajaxのコールバック関数
      function add_to_my_vocabulary_in_views($form, $form_state){
      
          global $user;
      
          $entity = $form_state['ch_vocabulary'] ;
      
          // DBにデータがある場合に削除処理
          if( $form_state['has_my_voca_result'] ) {
              db_delete( 'my_ch_vocabulary' )->condition( 'vid', $entity->vid )->condition('uid', $user->uid )->execute();
              //  チェックボックスのラベルを「Add」に変更
              return t('<span id="ch-voca-check-op-'.$vid.'">Add</span>');
          }
      
          // データがなければ、ch_vocabuaryをmy_vocabuaryにコピー
          $my_voca = entity_create('my_ch_vocabulary', array('vid'=>$entity->vid));
          $my_voca_wrapper = entity_metadata_wrapper( 'my_ch_vocabulary', $my_voca );
          $my_voca_wrapper->vocabulary->set( $ch_voca->vocabulary ) ;
          ....   //各種フィールド、プロパティのセット
          $my_voca_wrapper->save();   // コピーされたデータの保存
      
          //  チェックボックスのラベルを「Remove」に変更
          return  t('<span id="ch-voca-check-op-'.$vid.'">Remove</span>');
      }
  • hook_menu()が直接にajaxのチェックボックスフォームを呼び出す場合、ajaxの動作しません
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
            'title' => 'Copy and Remove',
            'page callback' => 'ajax_add_to_my_vocabulary',
            'page arguments' => array(array(), array(), 2) ,
            'access arguments' => array('create my vocabulary'),
      );
    }
    • ajaxのチェックボックスフォーム「ajax_add_to_my_vocabulary」が表示されますが、<form>要素がなくて、ajaxの動作はしません
      hook_menuが直接のフォームを呼び出す場合<form>要素が作成されずチェックボックスの動作はしない
  • hook_menu()が"drupal_get_form"を介してにajaxのチェックボックスフォームを呼び出す場合、ajaxの動作は正常です
    YOUR_MODULE_NAME_menu(){
      $items['your/path/%entity'] = array(
       'title' => 'Copy and Remove', 
       'page callback' => 'drupal_get_form', 
       'page arguments' => array('ajax_add_to_my_vocabulary', 2) , 
       'access arguments' => array('create my vocabulary'), 
      ); 
    }
    • 作成されたフォームに"<form>"要素およびajaxの要素が含まれているのでajaxが正常に動作します
      hook_menuが"drupal_get_form"を介してフォームを呼び出す場合に"<form>"要素が含まれてajaxが正常に動作

結論:表示系ページは直接に呼び出し、入力フォーム系は"drupal_get_form"を介して呼び出す

  • hook_menu()を実装時に、呼び出すページの性質によりページの呼び出す方法を決めます
    • 表示系ページ(データ更新なし)の場合は直接に呼び出せばよいです
    • 入力フォーム系のページは"drupal_get_form"を介して呼び出します
  • 表示系か、フォーム系かを区別せず、いつも"drupal_get_form"を介して呼び出しても構わないです
  • "drupal_get_form"がフォーム構築のプロセスが含まているので、フォームの要素構築するには必要です
    • drupa_get_form, drupal_prepare_form, drupal_retrieve_formの違い
    • これを使用する場合少し(どのぐらいは測定していないが)遅いかもしれないです
drupal
entity type
rules

Drupalのモジュール(EntityAPI)がモジュール(Rules、Viewsなど)に統合機能を提供していますが、各モジュール間の統一性が欠けています

  • 背景:Drupal7.56、EntityAPI7.x-1.8、Rules7.x-2.10
  • モジュール(EntityAPI)がほかのメジャーモジュール(Rules、Viewsなど)に統合機能を提供して、EntityAPIがよく利用されるようになります
  • ただし、各モジュール間の連携に統一性が欠けていることを気が付きました
    • 開発メンバーが違うので、開発ドキュメントが不備の場合、いろいろな誤解が生じるでしょう

例:エンティティ(Entity)へのアクセス権限管理にモジュール(Rules)が独自の権限("save")を生み出している

  • エンティティのアクセス権限チェック関数:entity_access($op, $entity_type, $entity = NULL, $account = NULL) (本家のentity_accessについての説明をご参考)
    • 引数$opは四つの権限:'view', 'update', 'create' or 'delete' (hook_entity_infoのパラメーター"access callback"を実装時にこの四つの権限を定義すればよいはずです)
  • モジュール(Rules)がエンティティアクセスチェック時に、"save"権限を生み出しています
    • rules/modules/entity_rules.incの313行あたモジュール(Rules)がエンティティに”save”権限を追加
    • 元々、モジュール(Rules)の"entity_save"は、「エンティティデータの保存」アクションです
    • ここで、"entity_save"を"save"に変換して、エンティティの"access callback"にパラメーターとして渡し、権限チェックをします
    • エンティティが定義している"access callback"に"save"権限を定義してなければ、権限がないと判断されます

対応方法1:モジュール(Rules)側でエンティティへのアクセス権限を再定義する

  • この例を限っては、モジュール(Rules)側で、エンティティへのアクセス権限の再定義します
    • Rulesの設定で「権限チェック省略」オプションがあるので、権限チェックを行わないことができます
    • Rulesの権限管理で各タイプのユーザーに権限を付与することもできます
      モジュール(Rules)でエンティティへのアクセス権限をユーザーに付与する設定

対応方法2:エンティティ側の"access callback"に"save"権限チェックを追加

  • hook_entity_info()の"access callback"の実装に、"save"権限を追加します("update"権限と同じはず)
    /**
     * Access callback for endity
     */
    function vocabulary_access($op, $entity, $account = NULL, $entity_type = NULL) {
        global $user;
    
        if (!isset($account)) {
            $account = $user;
        }
        switch ($op) {
            case 'create':
                return user_access('administer vocabulary', $account)
                || user_access('create vocabulary', $account);
            case 'view':
                return user_access('administer vocabulary', $account)
                || user_access('view vocabulary', $account);
            case 'edit':
            case 'save':    // save permission created by  entity_rule.inc
                return user_access('administer vocabulary')
                || user_access('edit any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
            case 'delete':
                return user_access('administer vocabulary')
                || user_access('delete any vocabulary')
                || (user_access('edit my own vocabulary') && ($entity->uid == $account->uid));
        }
    }
  • サードパーティーのモジュールでは、ソースコードの変更はしないほうがよいでしょう。対応方法1をお勧めします
drupal
entity type

hook_menu()でページアクセス権限設定("access callback")がない場合に「"access callback" => "user_access"」を自動に追加

  • hook_menu()がよく使用されると思います
  • URLへのアクセス権限管理例:Drupalのhook_menuでのユーザーロール別アクセス権限管理
  • もし、アクセス権限管理設定の”access callback”がなければ、menuシステムが実行時に「"access callback" => "user_access"」を自動的に追加します(この場合に”access arguments”が必要)
    menuシステムが「access callback」が定義されない場合に「user access」を自動的に呼び出す

hook_entity_info()を実装してエンティティ(Entity)へのアクセス管理はメニューシステムと同じ「"access callback"、"access arguments"」で行います

  • hook_entity_info()を実装して、エンティティ(Entity)へのアクセス管理はhook_menu()と同じように定義します
    /**
     * Implements hook_entity_info().
     */
    function drills_ch_vocabulary_entity_info()
    {
        return array(
            'my_vocabulary' => array(
                'label' => t('My Vocabulary'),
                'entity class' => 'MyVocabularyEntity',
                'controller class' => 'MyVocabulryController',
                'base table' => 'my_vocabulary',
                'fieldable' => TRUE,
                'entity keys' => array(
                    'id' => 'entity_id',
                    'label' => 'vocabulary',
                ),
                'bundles' => array(
                    'my_vocabulary' => array(
                        'label'=>'My Vocabulary',
                        'admin' => array(
                            'path' => 'admin/structure/my-vocabulary/manage',
                            'access arguments' => array('administer my vocabulary'),
                            'controller class' => 'EntityDefaultUIController',
                        ),
                    ),
                ),
                'view modes' => array(
                    'full' => array(
                        'label' => t('Default'),
                        'custom settings' => FALSE,
                    ),
                ),
                'module' => 'my_vocabulary',
                'access callback' => 'my_vocabulary_access',
            ),
        );
    }

エンティティ(Entity)へのアクセス権限チェックentity_access()関数が利用された場合は"access callback"の定義が必要です

  • EntityAPIの関数:entity_access()でユーザーのアクセス権限チェックに使用しているところが多いと思います
  • 実際に関数entity_access()のロジックは"access callback"設定のみしかチェックしていないです
    // entity/entity.module の659行あたり
    function entity_access($op, $entity_type, $entity = NULL, $account = NULL) {
      if (($info = entity_get_info()) && isset($info[$entity_type]['access callback'])) {
        return $info[$entity_type]['access callback']($op, $entity, $account, $entity_type);
      }
    }
    
  • hook_entity_info()で"access callback"が定義されていなければ、ここでのチェックが行わないことになります
  • 実際にモジュール(Views Bulk Operation)がエンティティのアクセス権限をチェックする際にentith_access()を使用しています
    • 自分が"access callback"をhook_entity_info()に定義していなくて、VBOの一括処理のチェックボックスが表示されなかったです(アクセス権限チェックが通れかなったせい)
      hook_entity_info()で"access callback"を定義しなければVBOの一括処理チェックボックスが表示表示されない
ホーム

古松

検索

Article Category

  • apache(7)
  • css(19)
  • drupal(295)
  • Electron(4)
  • html(34)
  • javascript(27)
  • laravel(4)
  • linux(5)
  • macOS(2)
  • mysql(13)
  • php(19)
  • python(4)
  • SEO(12)
  • video(72)
  • Visual Studio Code(4)
  • windows(13)
  • wordpress(32)