我想在我的一个WordPress插件中添加一个“单击下载”按钮,但不确定使用哪个钩子。到目前为止,将'admin_init'挂接到此代码似乎可行:

 header("Content-type: application/x-msdownload");
 header("Content-Disposition: attachment; filename=data.csv");
 header("Pragma: no-cache");
 header("Expires: 0");
 echo 'data';
 exit();


这似乎可行,但是我只想看看是否有最佳实践。

,谢谢,
戴夫

#1 楼

如果我正确理解了您的要求,那么您想要一个类似以下内容的URL,该URL对浏览器的响应将是您生成的内容,即您的.CSV文件,而不是WordPress生成的内容?
http://example.com/download/data.csv

我认为您正在寻找'template_redirect'挂钩。您可以在'template_redirect'中找到/wp-includes/template-loader.php,这是所有WordPress开发人员都应该熟悉的文件;它简短而有趣,可以路由每个非管理员页面加载,因此请务必对其进行观察。
只需将以下内容添加到主题的functions.php文件中,或添加到include中的functions.php中的另一个文件中:
add_action('template_redirect','yoursite_template_redirect');
function yoursite_template_redirect() {
  if ($_SERVER['REQUEST_URI']=='/downloads/data.csv') {
    header("Content-type: application/x-msdownload",true,200);
    header("Content-Disposition: attachment; filename=data.csv");
    header("Pragma: no-cache");
    header("Expires: 0");
    echo 'data';
    exit();
  }
}

通过检查'/downloads/data.csv'来记录对$_SERVER['REQUEST_URI'] URL的测试。还要注意在设置,true,200header()调用中添加的Content-type;这是因为WordPress将无法识别URL设置了404“未找到”状态代码。没问题,因为true告诉header()替换了WordPress设置的404并使用HTTP 200“ Okay”状态代码代替。
这就是FireFox的样子(请注意,屏幕截图没有/downloads/虚拟目录,因为在获取并注释了屏幕快照后,添加一个'/downloads/'虚拟目录似乎是一个好主意):
(来源:mikeschinkel.com)
UPDATE
如果您想要要从前缀为/wp-admin/的URL下载以进行处理,以便向用户直观地表明该内容受登录名的保护,您也可以这样做;一种方法的说明如下。
这次,我将其封装到一个名为DownloadCSV的类中,并为'download_csv'角色创建了一个名为'administrator'的用户“功能”(在此处了解角色和功能)。如果愿意,您可以背负预定义的'export'角色搜索并用'download_csv'替换'export',然后删除register_activation_hook()调用和activate()函数。顺便说一句,需要激活钩子是我将其移至插件而不是保留主题的functions.php文件的原因之一。*
我还从“工具”菜单中添加了“下载CSV”菜单选项使用add_submenu_page()并将其链接到'download_csv'功能。
最后我选择了'plugins_loaded'钩子,因为它是我可以使用的最早的合适钩子。您可以使用'admin_init',但是该钩子运行的时间要晚得多(第1130个钩子调用与第3个钩子调用),那么为什么要让WordPress做更多的一次性工作呢? (我使用了Instrument Hooks插件来找出要使用的钩子。)
在钩子中,我通过检查/wp-admin/tools.php变量来确保URL以$pagenow开头,我验证current_user_can('download_csv'),如果通过了,则测试$_GET['download']看看它是否包含data.csv;如果是,我们实际上运行与以前相同的代码。在上一个示例中,我还从对,true,200的调用中删除了header(),因为在此WordPress知道这是一个很好的URL,因此尚未设置404状态。所以这是您的代码:
<?php
/*
Plugin Name: Download CSV
Author: Mike Schinkel
Author URI: http://mikeschinkel.com
 */
if (!class_exists('DownloadCSV')) {
  class DownloadCSV {
    static function on_load() {
      add_action('plugins_loaded',array(__CLASS__,'plugins_loaded'));
      add_action('admin_menu',array(__CLASS__,'admin_menu'));
      register_activation_hook(__FILE__,array(__CLASS__,'activate'));
    }
    static function activate() {
      $role = get_role('administrator');
      $role->add_cap('download_csv');
    }
    static function admin_menu() {
      add_submenu_page('tools.php',    // Parent Menu
        'Download CSV',                // Page Title
        'Download CSV',                // Menu Option Label
        'download_csv',                // Capability
        'tools.php?download=data.csv');// Option URL relative to /wp-admin/
    }
    static function plugins_loaded() {
      global $pagenow;
      if ($pagenow=='tools.php' && 
          current_user_can('download_csv') && 
          isset($_GET['download'])  && 
          $_GET['download']=='data.csv') {
        header("Content-type: application/x-msdownload");
        header("Content-Disposition: attachment; filename=data.csv");
        header("Pragma: no-cache");
        header("Expires: 0");
        echo 'data';
        exit();
      }
    }
  }
  DownloadCSV::on_load();
}

这是激活的插件的屏幕截图:
(来源:mikeschinkel.com)
最后是触发下载的屏幕截图:
(来源:mikeschinkel.com)

评论


迈克,谢谢您的帮助。使用此功能的唯一问题是我希望从后端下载文件。看起来template_redirect在后端不起作用,并且如果我不应该使用admin_init,我想知道应该使用什么。 admin_init现在似乎对我有用,至少在短期内我可能会坚持使用它。这是一个很小的功能,只有少数人会使用。

–戴夫·莫里斯(Dave Morris)
2010年11月3日,下午2:37

@Dave Morris-您可以定义“后端”的含义吗?你是说在服务器上吗?如果是,那么'template_redirect'最肯定在服务器上运行。如果没有,我会很困惑。您可以澄清问题吗?提前致谢。

– MikeSchinkel
2010年11月3日12:00

@Dave:如果您用“后端”来表示管理区域,则该区域仍然有效。下载URL以/downloads/data.csv开头,这是一个不存在的文件,因此WordPress的“前端”将处理此请求并最终到达模板重定向。您只需在管理区域中创建一个指向该前端URL的链接。 (必须这样说,您不会免费获得管理员登录保护-任何知道URL的人都可以下载文件,但是也许有一种简单的方法可以解决该问题?)

– Jan Fabry
2010-11-3 20:41

@Jan Fabry-啊,我现在明白了。他是从管理员内部说的“后端”,对吧?他可以在上面的代码中使用函数current_user_can()或采取其他方法。发表评论后,我将在答案中添加更新。

– MikeSchinkel
10-11-4在9:55



是的,很抱歉,我没有收到来自此站点的电子邮件警报,因此可以解释我答复的延迟。当我说“后端”时,的确是指WordPress的管理区域。对于那个很抱歉。我将尝试使用template_redirect看看会发生什么。谢谢! 〜戴夫

–戴夫·莫里斯(Dave Morris)
2010-11-5 22:47

#2 楼

一个更有用的插件,用于导出为CSV。对某些人可能有用

    <?php

class CSVExport
{
/**
* Constructor
*/
public function __construct()
{
if(isset($_GET['download_report']))
{
$csv = $this->generate_csv();

header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private", false);
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=\"report.csv\";" );
header("Content-Transfer-Encoding: binary");

echo $csv;
exit;
}

// Add extra menu items for admins
add_action('admin_menu', array($this, 'admin_menu'));

// Create end-points
add_filter('query_vars', array($this, 'query_vars'));
add_action('parse_request', array($this, 'parse_request'));
}

/**
* Add extra menu items for admins
*/
public function admin_menu()
{
add_menu_page('Download Report', 'Download Report', 'manage_options', 'download_report', array($this, 'download_report'));
}

/**
* Allow for custom query variables
*/
public function query_vars($query_vars)
{
$query_vars[] = 'download_report';
return $query_vars;
}

/**
* Parse the request
*/
public function parse_request(&$wp)
{
if(array_key_exists('download_report', $wp->query_vars))
{
$this->download_report();
exit;
}
}

/**
* Download report
*/
public function download_report()
{
echo '<div class="wrap">';
echo '<div id="icon-tools" class="icon32">
</div>';
echo '<h2>Download Report</h2>';
//$url = site_url();

echo '<p>Export the Users';
}

/**
* Converting data to CSV
*/
public function generate_csv()
{
$csv_output = '';
$table = 'users';

$result = mysql_query("SHOW COLUMNS FROM ".$table."");

$i = 0;
if (mysql_num_rows($result) > 0) {
while ($row = mysql_fetch_assoc($result)) {
$csv_output = $csv_output . $row['Field'].",";
$i++;
}
}
$csv_output .= "\n";

$values = mysql_query("SELECT * FROM ".$table."");
while ($rowr = mysql_fetch_row($values)) {
for ($j=0;$j<$i;$j++) {
$csv_output .= $rowr[$j].",";
}
$csv_output .= "\n";
}

return $csv_output;
}
}

// Instantiate a singleton of this plugin
$csvExport = new CSVExport();


#3 楼

admin_init Hook或load-(page)Hook似乎有效,WordPress尚未在此状态下设置标头。
我正在使用load-(page)Hook,因为它在加载管理菜单页面时运行。 />您可以为特定页面加载脚本。

您可以检查WordPress Codex上的load-(page)钩子

如果您使用的是admin_init钩子,请确保进行验证使用check_admin_referer或其他脚本的随机数,可能会通过条件,然后输出您的下载文件。