Drupal 7 页面传递回调
或者,如何使用完全不同的模板呈现Drupal页面。
我最近有一个需求,我需要让Drupal呈现一个与网站的正常页面布局完全分开的HTML页面。这实际上是API回调的一部分,但这让我参与了研究Drupal7中传递回调的工作方式。没有必要仅仅为了渲染带有一些自定义HTML的单个页面的工作而创建一个新主题,尤其是因为Drupal有提供这种内置的机制。
如果您做过很多Drupal编程,您可能已经使用了传递回调,但实际上并没有意识到它。调用函数喜欢drupal_access_denied()和drupal_not_found()会分别发出403或404页,但他们通过烧成页面,这使得交付回调的工作,然后发送页面输出到浏览器。这些函数通过调用函数来工作,drupal_deliver_page()函数又会触发当前选择的传递回调函数被调用。默认情况下,thisisdrupal_deliver_html_page(),它使用当前主题呈现HTML输出,添加适当的标题,并将此内容发送到浏览器。
有两种方法可以更改传递回调,使用哪一种取决于您的需要。
实施hook_page_delivery_callback_alter()
就在drupal_deliver_page()finally调用之前drupal_deliver_html_page(),安装的模块有机会通过hook_page_delivery_callback_alter()钩子改变传递回调。这个钩子允许你根据某些参数改变传递回调函数。下面显示了用于更改站点首页的传递回调函数的钩子。稍后我们将介绍自定义交付功能的内容。
/** * Implements hook_page_delivery_callback_alter(). */ function mymodule_page_delivery_callback_alter(&$delivery_callback) { if (drupal_is_front_page()) { $delivery_callback = 'mymodule_deliver_page'; } }
这个钩子不仅可以用来改变页面模板。因为它是在页面回调运行之后调用的,但在页面(以及大部分,如果不是全部内容)呈现之前,它是在页面呈现之前更改某些内容的好方法。例如,我发现这个钩子是改变页面菜单位置的好方法。以以下愚蠢的示例为例,这会移动Cron设置页面,使其看起来位于模块页面下方。
/** * Implements hook_page_delivery_callback_alter(). */ function mymodule_page_delivery_callback_alter(&$delivery_callback) { if (implode(arg(), '/') == 'admin/config/system/cron') { menu_tree_set_path('management', 'admin/modules'); } }
这本身并没有用,但是让某些页面出现在某些菜单项下是难以正确解决的常见Drupal问题之一。这个钩子提供了一个很好的方法来做到这一点。
菜单传递回调
提供自定义回调的第二种方法是使用菜单挂钩项的传递回调属性。此选项意味着菜单回调的输出将自动传递到此传递回调,而无需定义任何挂钩。以下示例在菜单挂钩和非常简单的页面回调函数中显示了这一点。
function mymodule_menu() { $items['mymodule/page'] = array( 'title' => 'A Page', 'page callback' => 'mymodule_return', 'access callback' => TRUE, 'delivery callback' => 'mymodule_deliver_page', 'type' => MENU_CALLBACK, ); return $items; } function mymodule_return() { return '' . t('The current timestamp is') . ' : ' . time().'
'; }
这个菜单页面回调不需要做任何特别的事情,但它产生的所有内容都被馈送到传递回调函数。页面回调函数也不需要有什么特别之处,它可以像往常一样返回一个字符串或一个Drupal渲染数组。在当前状态下,上面的代码将产生一个白屏,并且在看门狗表中将看到错误消息“callbackmymodule_deliver_pagenotfound:mymodule/page”。这是因为缺少deliver回调函数,所以我们来看看创建一个。
创建交付回调函数
为了使上述代码示例正常工作,您需要创建一个传递回调函数。用最简单的术语来说,传递回调函数必须简单地打印出页面的内容。下面虽然简单,但满足了这个要求。
function mymodule_deliver_page($page_callback_result) { print render($page_callback_result); }
有了这个,上面的代码示例现在可以工作了。尽管页面将是空白的,因为这仅提供内容,而没有其他HTML或样式。
在创建您自己的传递回调之前,了解Drupal在这方面的工作方式很重要。Drupal7中HTML页面的传递回调函数被调用drupal_deliver_html_page().这个函数在这里重现有点太长了,但是它通过适应Drupal在页面渲染过程中返回的不同类型的变量来工作。如果页面回调返回一个整数,则该函数会将其视为错误,并将使用错误代码返回该错误的正确标题和页面。这是一个有用的效果,因为它意味着要生成访问被拒绝的页面,您只需要从页面回调中返回MENU_ACCESS_DENIED常量。MENU_ACCESS_DENIED是一个整数常量,这意味着Drupal将返回403HTTP状态代码和访问被拒绝页面。如果页面回调返回一个字符串或数组,则将其传递给drupal_render_page()函数,该函数将正常呈现页面。
我们可以将drupal_deliver_html_page()功能减少到最基本的要求,并在单个模板中呈现我们的页面内容。下面将在一个简单的HTML结构中呈现页面内容之前发出几个标题。最后的drupal_page_footer()函数是一个Drupal实用程序函数,它编写会话并调用任何关闭函数,这些函数应该始终在页面末尾调用。
function mymodule_deliver_page($page_callback_result) { if (is_null(drupal_get_http_header('Content-Type'))) { drupal_add_http_header('Content-Type', 'text/html; charset=utf-8'); } //为浏览器和搜索引擎发送适当的HTTP标头。 global $language; drupal_add_http_header('Content-Language', $language->language); //呈现页面和内容 print ''; print render($page_callback_result); print ''; drupal_page_footer(); }
为了使其更易于维护,您显然会将其包装在主题函数中,但此示例作为一个简单示例工作。我使用了上述代码的一个版本(通过模板文件传递输出)来生成API回调所需的输出。这里的结果是,如果输出由于某种原因需要更改,那么所有需要更改的就是模板文件,其他所有内容都由模块处理。
还要记住,我们在这里基本上绕过了Drupal访问被拒绝的逻辑,因此您需要非常确保您返回的输出是合适的。如果您使用hook_page_delivery_callback_alter()钩子来改变页面交付,这一点尤其重要。您可以很容易地打开未发布的内容或用户个人资料的详细信息,以便匿名查看,所以要小心!
返回JSON输出
也许对传递回调最有用的事情是以不同的格式返回输出。这可以通过稍微调整mymodule_deliver_page()函数来轻松完成,以便它侦听不同类型的标题,为遇到的每种mime类型稍微调整输出。
language); switch ($content_type) { case 'application/json': print json_encode($page_callback_result); break; case 'application/xml': print '' . render($page_callback_result) . ' '; break; default: print ''; print render($page_callback_result); print ''; } drupal_page_footer(); }
为了生成JSON输出,我们现在需要做的就是确保页面回调设置一个“application/json”标头。
function mymodule_return() { drupal_add_http_header('Content-Type', 'application/json'); return array(t('The current timestamp is') . ' : ' . time()); }
这将生成以下输出。
["Thecurrenttimestampis:1440147347"]
我们还可以通过一些简单的更改切换到XML输出。
function mymodule_return() { drupal_add_http_header('Content-Type', 'application/xml'); return t('The current timestamp is') . ' : ' . time(); }
这使得在Drupal中生成简单的API服务变得非常容易。Drupal假定您的内容将是HTML,但是通过几行代码,您可以让Drupal以您喜欢的任何格式返回内容。