Jak powiedzieć Drupalowi, aby szukał szablonów w katalogu modułów?

11

Chciałbym zapewnić implementację szablonu w moim module i pozwolić motywom na zastąpienie go. Zasadniczo dodaję sugestię za pomocą tego uproszczonego kodu:

function attach_preprocess_node(&$vars) {
  $vars['theme_hook_suggestions'][] = 'node__test';
}

(Nie chcę używać hook_theme do dodawania nowego motywu, ponieważ chcę ponownie użyć funkcji węzła preprocesowania. Nazwa motywu jest niezręczna, ale nie chcę pisać node_ attach _%, aby uniknąć pomyłki z typem węzła).

Następnie używam hook_theme_registry_alter (), aby dodać ścieżkę modułu:

function attach_theme_registry_alter(&$theme_registry) {
  $path = drupal_get_path('module', 'attach') . '/themes';
  $theme_registry_copy = $theme_registry;
  _theme_process_registry($theme_registry_copy, 'phptemplate', 'theme_engine', 'node', drupal_get_path('module', 'node'));
  $theme_registry += array_diff_key($theme_registry_copy, $theme_registry);
  if (!isset($theme_registry['node']['theme paths'])) {
    $theme_registry['node']['theme paths'] = array();
  }
  if (!isset($theme_registry['node']['theme paths'])) {
    $first_element = array_shift($theme_registry['node']['theme paths']);
    if ($first_element) {
      array_unshift($theme_registry['node']['theme paths'], $first_element, $path);
    }
    else {
      array_unshift($theme_registry['node']['theme paths'], $path);
    }
  }
}

Jednak to nie działa. To znaczy: plik themes / node - super.tpl.php nie jest używany. Jest używany tylko wtedy, gdy skopiowałem go do folderu motywu.

Jcisio
źródło
Wyjaśnienie: Chcę, aby Drupal szukał w katalogu modułów (a także w katalogach motywów) szablonu zdefiniowanego przez kod (tutaj: szablon węzła). Nie chcę definiować nowego szablonu.
jcisio

Odpowiedzi:

5

Zasadniczo możesz zaoszczędzić sobie trochę bólu głowy, wdrażając hook_theme()zamiast modyfikować rejestr.

Proponuję rzucić okiem na theming_example w projekcie przykładów , ręcznie odtworzonym na tej stronie dokumentu API , być może ze szczególnie pomocnym kodem na tej stronie .

function theming_example_list_page() {
  $items = array(
    t('First item'),
    t('Second item'),
    t('Third item'),
    t('Fourth item'),
  );

  // First we'll create a render array that simply uses theme_item_list.
  $title = t("A list returned to be rendered using theme('item_list')");
  $build['render_version'] = array(
    // We use #theme here instead of #theme_wrappers because theme_item_list()
    // is the classic type of theme function that does not just assume a
    // render array, but instead has its own properties (#type, #title, #items).
    '#theme' => 'item_list',
    // '#type' => 'ul',  // The default type is 'ul'
    // We can easily make sure that a css or js file is present using #attached. 
    '#attached' => array('css' => array(drupal_get_path('module', 'theming_example') . '/theming_example.css')), 
    '#title' => $title, 
    '#items' => $items, 
    '#attributes' => array('class' => array('render-version-list')),
  );

  // Now we'll create a render array which uses our own list formatter,
  // theme('theming_example_list').
  $title = t("The same list rendered by theme('theming_example_list')");
  $build['our_theme_function'] = array(
    '#theme' => 'theming_example_list', 
    '#attached' => array('css' => array(drupal_get_path('module', 'theming_example') . '/theming_example.css')), 
    '#title' => $title, 
    '#items' => $items,
  );
  return $build;
}

To wszystko dotyczy Drupala 7.

paul-m
źródło
Jak powiedziałem w pytaniu, nie chcę używać hook_theme (), ponieważ chcę ponownie użyć zmiennych $ w szablonie węzła. Te zmienne są generowane w procesie hook_ (pre) wielu modułów, które nie wiedzą o istnieniu mojego motywu (jeśli go zdefiniuję).
jcisio
Sposób definiowania motywu jest ... za pomocą hook_theme (). :-) Możesz zdefiniować funkcje tematyczne w hook_theme (). Nazwij je, jak chcesz. Wykonuj funkcje podkładek, jeśli chcesz. Dokumenty API: „hook_theme_HOOK () ... należy używać tylko wtedy, gdy moduł musi przesłonić lub dodać do wstępnego przetwarzania kompozycji dla haka motywu, którego nie zdefiniował”.
paul-m
Nie chcę definiować motywu. Jeśli zdefiniujesz „mynode” motywu zamiast ponownie używać „węzła”, w pliku .tpl.php nie będzie żadnych zmiennych.
jcisio
1
To nie wydaje się prawdą: wywołania tematyczne są kumulatywne, więc implementacja hook_themepowinna dać ci $existingparametr, który pozwala modyfikować rzecz, a nie przesłonić ją. Jeśli tak nie jest, być może trafiłeś w błąd.
Countzero
@Countzero podczas deklarowania kompozycji node_attach, wówczas wszystkie funkcje hook_preprocess_node nie będą działać z nowym motywem. Tzn. Nie masz nic w swoim węźle-attach.tpl.php.
jcisio
5

Może ten działa:

/**
 * Implements hook_theme().
 */
function MODULE_theme($existing, $type, $theme, $path) {
  return array (
    'node__CONTENTTYPE' => array (
      'variables' => array( . . . ),
      'template' => 'node--CONTENTTYPE' ,
      'base hook' => 'node',
      'path' => drupal_get_path('module', 'MODULE'),
    ),
  );
}

Ważne jest tutaj kluczowe „ hak podstawowy ”.

dashohoxha
źródło
Oto problem z dodaniem dokumentacji dla base hook: drupal.org/node/2106635
Andy
+1 upvote - ta i pochodna odpowiedź z batigotix, które znalazłem, by działać. Dzięki.
therobyouknow
2

Lubię rozwiązanie dashohoxha implementacji hook_theme, ale nie udało mi się go uruchomić. Po kilku kolejnych googlingach znalazłem odmianę, która działała dla mnie dobrze:

/**
 * Implements hook_theme().
 */
function mymodule_theme($existing, $type, $theme, $path) {
  $theme = array();
  $theme['node__blog_post'] = array(
    'render element' => 'content',
    'base hook' => 'node',
    'template' => 'node--blog_post',
    'path' => drupal_get_path('module', 'mymodule') . '/templates',
   );
  return $theme;
}

Uwagi: mój niestandardowy moduł nazywa się „mymoduł”, a mój niestandardowy typ zawartości to „blog_post”. Używany przeze mnie tpl.php nazywa się „node - blog_post.tpl.php” i znajduje się w podfolderze „templates” mojego modułu.

batigolix
źródło
+1 dzięki znalazłem, że to działa. Jeśli chcesz również zastąpić funkcje template.php w swoim module niestandardowym, spójrz na: snugug.com/musings/override-theme-functions-drupal-7-module - Przekonałem się, że to działa bardzo dobrze
therobyouknow
2

Oto mój fragment kodu do deklarowania szablonów widoków przechowywanych w folderze „template” mojego „custom_module”:

/**
 * Implements hook_theme_registry_alter().
 */
function custom_module_theme_registry_alter(&$theme_registry) {
  $extension   = '.tpl.php';
  $module_path = drupal_get_path('module', 'custom_module');
  $files       = file_scan_directory($module_path . '/templates', '/' . preg_quote($extension) . '$/');

  foreach ($files as $file) {
    $template = drupal_basename($file->filename, $extension);
    $theme    = str_replace('-', '_', $template);
    list($base_theme, $specific) = explode('__', $theme, 2);

    // Don't override base theme.
    if (!empty($specific) && isset($theme_registry[$base_theme])) {
      $theme_info = array(
        'template'   => $template,
        'path'       => drupal_dirname($file->uri),
        'variables'  => $theme_registry[$base_theme]['variables'],
        'base hook'  => $base_theme,
        // Other available value: theme_engine.
        'type'       => 'module',
        'theme path' => $module_path,
      );

      $theme_registry[$theme] = $theme_info;
    }
  }
}

Mam nadzieję, że to komuś pomoże.

Sebastien M.
źródło
oszczędził mi czas. Działa również i wymagane dla d8
Mykoła Mikołajowicz Dolynskyi
-1

Zapytałem o to raz na Stack Overflow . Zasadniczo musisz wdrożyć, hook_theme_registry_alter()aby dodać ścieżkę do ścieżki szablonu zaczepu motywu; następnie hook_enable()wywołujesz drupal_theme_rebuild (), aby wyczyścić pamięć podręczną rejestru motywów i przeskanować ścieżkę w poszukiwaniu szablonów.

Capi Etheriel
źródło
Może to, co Cię powstrzymuje, to po prostu czyszczenie pamięci podręcznej.
Capi Etheriel
Zasadniczo to samo rozwiązanie. Próbowałem „drush cc all” nie mniej niż 50 razy, bez powodzenia przetestowałem go na nowej stronie instalacyjnej itp. Zmodyfikuję i skompresuję mój kod do minimalnego modułu, aby każdy mógł go przetestować.
jcisio
hook_enable()jest wywoływany, gdy moduł jest włączony; jeśli moduł jest już włączony, należy go wyłączyć, a następnie włączyć ponownie.
kiamlaluno
@kiamlaluno: iḿ używam hook_enable do czyszczenia pamięci podręcznej, jeśli jest już zainstalowany, użytkownik może po prostu wyczyścić pamięć podręczną ręcznie.
Capi Etheriel
NIE , -1 punktów. To rozwiązanie jest tak stare (2009), że nawet nie jestem pewien, czy było przeznaczone dla D7. Chociaż stare rozwiązanie jest dostosowane do widoków, nie jest idealne w sytuacjach innych niż widoki, w których programiści mogą chcieć spakować więcej niż 1 szablon domyślny na klucz motywu w swoich modułach. Wyobraź sobie wdrożenie swojego rozwiązania dla 100 sugestii dynamicznych motywów dla jednego klucza motywu. Zastosowany poza kontekstem Widoków nazwałbym twoje rozwiązanie kontr-wzorem.
amator barista