WordPress-插件开发秘籍-全-

WordPress 插件开发秘籍(全)

原文:zh.annas-archive.org/md5/a6a9cf87cba4c364fda60a2008bccb40

译者:飞龙

协议:CC BY-NC-SA 4.0

前言

如果你是一名管理员,想要为个人网站添加不存在于任何插件中的自定义功能,或者是一名开发者,想要为 WordPress 平台带来新的社区想法,或者是一名为特定客户构建项目的网站设计师,那么为 WordPress 开发插件对你来说将是下一件大事。学习如何创建 WordPress 插件将使你能够充分发挥最流行的网络内容管理系统(CMS)的潜力。

作为 WordPress 的早期使用者,在 1.0 版本发布之前,我开始构建插件来为我的个人网站添加功能。一旦我设置了这些新元素,我很快意识到其他用户也能从这些扩展中受益,并开始在线分发它们。时至今日,我依然喜欢收到用户对我的作品的反馈,了解他们如何使用它们以及他们认为哪些新功能可以使它们变得更好。

虽然开发插件最初可能听起来有点像黑魔法,但本书通过一系列分步指南展示了创建插件实际上是多么简单。如果你之前曾向主题的功能文件中添加过代码,那么你甚至可能对本书中解释的一些机制有所了解。本书包含了所有相关信息,你将能够快速创建自己的插件或分析现有的插件,以添加你所需要的额外功能。不久之后,你将能够将你的作品发布到官方 WordPress 插件仓库!

让我们开始学习如何制作出色的 WordPress 插件!

本书涵盖的内容

第一章,准备本地开发环境,展示了插件开发者如何安装和配置一个高效的开发环境。

第二章,插件框架基础,解释了如何将用户函数注册到 WordPress 中,以便在网页显示的关键点执行,这是插件创建的基础。

第三章,用户设置和管理页面,涵盖了创建允许用户配置你创建的插件的行政页面。

第四章,自定义文章类型的威力,赋予开发者向 WordPress 环境添加全新的内容管理部分的能力。

第五章,自定义文章和页面编辑器,展示了如何修改默认的管理文章和页面编辑环境,以添加新的功能。

第六章,接受用户内容提交,允许用户向由你的插件管理的新内容部分提交自己的内容。

第七章,自定义用户数据,解释了如何存储用户额外信息以及如何根据这些数据修改网站输出。

第八章创建自定义 MySQL 数据库表,利用 MySQL 的力量在网站数据库中创建自定义数据库表以存储和检索自定义数据。

第九章,利用 JavaScript、jQuery 和 AJAX 脚本,通过使用多个流行的脚本库,使插件输出非常动态。

第十章,向 WordPress 库添加新小工具,说明了如何添加用户可以轻松拖放以向网页添加内容的新小工具。

第十一章,启用插件国际化,使您的插件能够翻译成任何语言,以便非英语使用者更容易使用。

第十二章,在 wordpress.org 上分发您的插件,展示了如何为与全球 WordPress 社区共享您的插件做准备。

你需要这本书的内容

第一章,准备一个本地开发环境,将引导你了解在为 WordPress 插件开发时需要具备的所有有用工具,包括本地网络服务器、Subversion 客户端和专门的代码编辑器。虽然这本书将始终描述执行其食谱所需的全部步骤,但如果你对 WordPress 有良好的理解,将能充分欣赏这些页面中的信息。

这本书面向谁

这本书是为具有 WordPress 和 PHP 基础知识、对创建新插件以满足个人需求、客户需求或与 WordPress 社区分享新想法感兴趣的 WordPress 用户、开发人员或网站集成人员而编写的。

术语约定

在这本书中,你会发现许多文本样式,用于区分不同类型的信息。以下是一些这些样式的示例及其含义的解释。

文本中的代码单词、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 昵称如下所示:“在ch8-bug-tracker目录下创建一个名为uninstall.php的文本文件,并在代码编辑器中打开它。”

代码块如下设置:

add_filter( 'the_generator', 'ch2gf_generator_filter', 10, 2 );

当我们希望引起您对代码块中特定部分的注意时,相关的行或项目将以粗体显示:

 echo '<tr style="background: #FFF">'; 
 echo '<td><input type="checkbox" name="bugs[]" value="'; 
 echo intval( $bug_item['bug_id'] ) . '" /></td>'; 
 echo '<td>' . $bug_item['bug_id'] . '</td>';

新术语重要词汇以粗体显示。你在屏幕上看到的单词,例如在菜单或对话框中,在文本中如下所示:“在这里,我们添加了一个包含两个全尺寸列的内嵌网格;一个用于项目的价格,另一个将包含数量组件。”

警告或重要注意事项以如下框的形式出现。

小技巧和窍门如下所示。

读者反馈

我们始终欢迎读者的反馈。告诉我们您对这本书的看法——您喜欢或不喜欢什么。读者反馈对我们很重要,因为它帮助我们开发出您真正能从中获得最大价值的标题。

为了发送给我们一般性的反馈,请简单地发送电子邮件至feedback@packtpub.com,并在邮件主题中提及书籍的标题。

如果您在某个主题上具有专业知识,并且您有兴趣撰写或为书籍做出贡献,请参阅我们的作者指南www.packtpub.com/authors

客户支持

现在您已经是 Packt 图书的骄傲拥有者,我们有一些事情可以帮助您从您的购买中获得最大价值。

下载示例代码

您可以从您的账户中下载此书的示例代码文件www.packtpub.com。如果您在其他地方购买了此书,您可以访问www.packtpub.com/support并注册,以便将文件直接通过电子邮件发送给您。

您可以通过以下步骤下载代码文件:

  1. 使用您的电子邮件地址和密码登录或注册我们的网站。

  2. 将鼠标指针悬停在顶部的 SUPPORT 标签上。

  3. 点击代码下载与勘误。

  4. 在搜索框中输入书籍的名称。

  5. 选择您想要下载代码文件的书籍。

  6. 从下拉菜单中选择您购买此书籍的地方。

  7. 点击代码下载。

文件下载完成后,请确保使用最新版本的以下软件解压或提取文件夹:

  • WinRAR / 7-Zip for Windows

  • Zipeg / iZip / UnRarX for Mac

  • 7-Zip / PeaZip for Linux

本书代码包也托管在 GitHub 上,网址为github.com/PacktPublishing/WordPress-Plugin-Development-Cookbook-Second-Edition。我们还有其他来自我们丰富图书和视频目录的代码包,可在github.com/PacktPublishing/找到。查看它们吧!

勘误

尽管我们已经尽最大努力确保内容的准确性,但错误仍然可能发生。如果您在我们的书籍中发现错误——可能是文本或代码中的错误——如果您能向我们报告这一点,我们将不胜感激。通过这样做,您可以避免其他读者的挫败感,并帮助我们改进本书的后续版本。如果您发现任何勘误,请通过访问www.packtpub.com/submit-errata,选择您的书籍,点击勘误提交表单链接,并输入您的勘误详情。一旦您的勘误得到验证,您的提交将被接受,勘误将被上传到我们的网站或添加到该标题的勘误部分下的现有勘误列表中。

要查看之前提交的勘误表,请访问www.packtpub.com/books/content/support,并在搜索字段中输入书籍名称。所需信息将出现在勘误表部分。

海盗行为

在互联网上对版权材料的盗版是一个横跨所有媒体的持续问题。在 Packt,我们非常重视我们版权和许可证的保护。如果您在互联网上发现任何形式的我们作品的非法副本,请立即提供位置地址或网站名称,以便我们可以寻求补救措施。

请通过链接将涉嫌盗版材料发送至copyright@packtpub.com

我们感谢您在保护我们作者和我们为您提供有价值内容的能力方面的帮助。

问答

如果您对本书的任何方面有问题,您可以联系我们的questions@packtpub.com,我们将尽力解决问题。

第一章:准备本地开发环境

我们在本章中将涵盖以下主题:

  • 在您的计算机上安装 Web 服务器

  • 下载和配置本地 WordPress 安装

  • 在本地 Subversion 仓库中创建

  • 将初始文件导入到本地 Subversion 仓库

  • 从 Subversion 仓库检出文件

  • 将更改提交到 Subversion 仓库

  • 安装专门的代码/文本编辑器

简介

在我们开始编写第一个 WordPress 插件之前,拥有一个良好的工具集非常重要,这将使您能够在本地计算机上工作并提高工作效率。虽然使用操作系统提供的内置工具执行一些开发任务是可能的,但创建一个稳固的本地开发环境将帮助您快速开发插件,并能够完全控制服务器设置以测试不同的配置。

本章提出了一套免费工具,可以轻松安装在您的计算机上,无论您首选的操作系统是什么,以方便您开发未来的 WordPress 插件。这些工具包括一个本地 Web 服务器,以加快页面访问速度并避免不断将文件发送到远程服务器,一个版本控制系统以保留您工作的增量备份,以及一个代码编辑器以增强您的编辑能力。除了安装和学习如何使用这些工具外,本章还展示了如何在本地 Web 服务器上下载和配置本地 WordPress 安装。

在您的计算机上安装 Web 服务器

配置本地开发环境的第一个步骤是在您的计算机上安装一个本地 Web 服务器。这将使您的计算机成为一个能够显示网页并执行与本地渲染 WordPress 网站相关的所有任务的系统。

拥有本地 Web 服务器有许多好处,如下所示:

  • 由于所有信息都是本地处理的,因此它提供了对频繁页面刷新的快速响应,这些刷新是在编写、测试和改进插件代码时进行的

  • 它消除了不断将新的插件文件版本上传到远程 Web 服务器以验证代码更改的需求

  • 它允许在没有互联网连接的情况下进行开发(例如,在乘坐飞机旅行时)

  • 它提供了一个无忧的编程环境,您不会因为编程错误或无限循环而使实时网站崩溃

在线有许多免费包,包含运行 WordPress 安装所需的所有 Web 服务器组件。本食谱向您展示了如何轻松安装这些包之一。

如何操作...

  1. 访问 XAMPP 网站(www.apachefriends.org/)并下载适用于您的计算机的相应 XAMPP 包。

XAMPP 适用于 Windows、macOS 和 Linux 平台。本食谱中的截图是在 Windows 10 上的 XAMPP 版本 5.6.30 中拍摄的。安装步骤和确切对话框内容可能因您选择的平台而异。

  1. 在 Windows 上可选:禁用 Windows 用户访问控制(UAC)功能,以便 XAMPP 可以完全权限安装到您的系统上(在您喜欢的搜索引擎中查找执行此步骤的步骤)。

  2. 启动 XAMPP 安装程序(Windows 平台上的xampp-win32-5.6.30-0-VC11-installer.exe)。

  3. 确认关于用户访问控制UAC)的警告信息,然后点击“下一步”以开始安装过程。

  4. 在下一个屏幕上,列出了所有可以安装的组件,取消选中 FileZilla FTP 服务器、Mercury 邮件服务器、Tomcat、Perl 和 Webalizer 的复选框,然后点击“下一步”:

图片

  1. 在安装文件夹屏幕上,如果可能,保留默认的安装目录值(c:\xampp),因为本书中会引用此文件夹,然后点击“下一步”。

  2. 点击“下一步”按钮以继续进行 Web 服务器的安装。

  3. 确保已勾选启动控制面板的选项,并在安装完成后点击“完成”。

  4. 选择您首选的语言用于 XAMPP 控制面板,然后点击“保存”以启动应用程序。

  5. 点击 Apache 和 MySQL 的“启动”按钮以启动这些模块。一旦成功启动,它们的名称将如以下截图所示变为绿色高亮:

图片

  1. 打开一个网页浏览器,导航到地址http://localhost以显示您本地 Web 服务器的欢迎页面:

图片

  1. 在文本编辑器中打开c:\xampp\apache\conf\httpd.conf文件(例如,记事本)。

  2. 搜索DocumentRoot配置选项,并将其值更改为磁盘上的不同位置,以避免将项目文件保留在原始安装目录下。例如,您可以将其设置为一个新的目录,用于存放您的本地 WordPress 开发安装,如DocumentRoot "C:/WPDev"

注意在这个路径中使用的是正斜杠。如果您从文件资源管理器窗口复制粘贴路径,请务必小心。

  1. 搜索Directory选项,并将其更改为与DocumentRoot相同的路径,即<Directory "C:/WPDev">

  2. 保存并关闭httpd.conf文件。

  3. 如果指定的目录DocumentRoot在您的计算机上不存在,请创建该目录。

  4. 打开 XAMPP 控制面板。

  5. 停止并重新启动 Apache 服务,以便新配置生效。

在执行步骤 14 至 20 之后,尝试访问本地 Web 服务器的欢迎页面将不再工作,因为新指定的目录目前是空的。这将在下一个食谱中纠正。

它是如何工作的...

XAMPP 软件包包含运行 Web 服务器所需的所有组件,该服务器可以在您的计算机上托管 WordPress 网站。这些组件包括:

  • Apache Web 服务器

  • PHP 解释器

  • MySQL 数据库服务器

  • phpMyAdmin 数据库管理界面

XAMPP 软件包还包括其他组件,这些组件不是运行本地 WordPress 开发站点所必需的。

一旦 XAMPP 安装并启动,我们在网页浏览器中输入的localhost关键字就会被操作系统识别为与本地计算机上的 Web 服务器进行通信的请求,Apache Web 服务器会显示其文档中的欢迎页面。

XAMPP 文档是一组位于 Windows 平台c:\xampp\htdocs目录中的平面 HTML 文件。这是 Web 服务器的默认工作目录。

该指南的最后几个步骤指示 Apache Web 服务器在新的目录中查找本地网站的文件内容。这是一个安全预防措施,以确保在 XAMPP 卸载时不会意外删除网站文件。它还可以帮助在单台计算机上管理多个网站。

更多内容...

虽然 XAMPP 是一个功能齐全的本地 Web 服务器软件包,并且可在三大操作系统上使用,但网上还有许多其他软件包。大多数这些软件包将直接在计算机上运行所需的 Web 服务,而更高级的软件包,如Varying Vagrant VagrantsVVV),将在您的计算机上虚拟化基于 Linux 的 Web 服务器,以创建一个针对 WordPress 优化的最终部署环境的更精确副本。以下是其中一些最受欢迎的本地 Web 服务器软件包列表:

对于 Windows 系统:

对于 macOS X:

对于 Windows、Mac 或 Linux 系统:

要获取更完整的 Web 服务器软件包列表,请访问en.wikipedia.org/wiki/List_of_AMP_packages

创建远程 Web 开发环境

如果您无法设置本地 Web 服务器来开发 WordPress 插件,或者您计划与一或多人共享开发任务,那么设置本地 Web 服务器的替代方案是创建一个远程开发环境。

假设您已经设置了 Web 托管账户,创建主域名的子域是创建此类环境的最简单方法。这将允许您创建一个独立的 WordPress 测试安装,同时仍然可以保护实时站点不受影响,但不会带来本地安装的其他好处。

参见

  • 下载和配置本地 WordPress 安装指南

下载和配置本地 WordPress 安装

我们本地开发环境的下一个组件是在您的本地网络服务器上安装 WordPress,以运行一个完全工作的网站,并且所有文件都本地托管。

WordPress 一直以其简单的五分钟安装过程而自豪。在本地网络服务器上安装它甚至比在实时远程服务器上安装还要简单快捷。本配方涵盖了创建 MySQL 数据库以存储与我们的新 WordPress 安装相关的所有数据的步骤,以及实际的设置过程。

准备工作

此配方假设您已经在计算机上安装了本地网络服务器。这个网络服务器可以是使用之前的配方执行的新安装,也可以是从之前的安装中来的。以下章节中的步骤是针对新本地网络服务器编写的。如果您为访问 MySQL 数据库创建了新账户或更改了 root 用户的密码,一些步骤会有所变化。如果您使用的是不同于 XAMPP 的其他网络服务器,phpMyAdmin 工具的位置也可能不同。您应该参考您的网络服务器文档以找出该地址。

如何操作...

  1. 在网络浏览器中,导航到地址 http://localhost/phpmyadmin/ 以访问您的网络服务器数据库管理工具。

  2. 在 phpMyAdmin 中点击“数据库”标签页。

  3. 在“创建数据库”一词后面的空白字段中输入要创建的新数据库的名称。在本例中,我们将使用名称 wordpressdev

图片

  1. 点击“创建”按钮以完成数据库创建过程。

  2. 从官方 WordPress 网站下载最新的 WordPress 安装包(wordpress.org)。下载链接可以在网站的首页找到,下载包可以在任何网络服务器上运行,无论是本地还是远程。

以下说明已在 WordPress 版本 4.8 上进行了测试。虽然安装过程在不同版本之间通常变化不大,但在较新版本中,这些步骤可能会有细微差异。

  1. 使用您喜欢的文件归档实用程序或操作系统的内置功能提取 WordPress 归档文件的内容。

  2. 将生成的 wordpress 文件夹的内容复制到您的本地网络服务器的网络内容目录(如果您遵循了之前的配方,则为 c:\WPDev)。除非您想使您的 WordPress 网站地址为 http://localhost/wordpress,否则您不应该复制 wordpress 文件夹本身。

  3. 将您的网络浏览器指向 http://localhost 以启动 WordPress 安装过程。

  4. 选择您首选的语言并点击继续。

  5. 在下一页,点击“让我们开始”按钮以启动您开发网站的配置。

  6. 更新数据库名称字段以反映我们新创建的数据库的名称(wordpressdev)。

  7. 将 MySQL 用户名设置为 root

  8. 将 MySQL 密码中的所有字符删除,使其为空,因为本地 MySQL 服务器 root 账户通常配置为没有任何密码。

  9. 保持数据库主机字段使用其默认值(localhost)。

  10. 将表前缀字段从其默认值更改为wpdev_

图片

  1. 点击“提交”按钮以验证输入的信息。如果任何参数输入不正确,或者 WordPress 安装过程无法正确访问您的数据库服务器,它将显示错误页面,并给您机会进行更正。

  2. 点击“运行安装”按钮以创建在指定 MySQL 数据库中的所需表结构。

  3. 指定站点标题(例如,开发站点)。

  4. 为管理员用户设置用户名。为了提高安全性,最好选择一个人们难以轻易猜到的用户名。应避免使用明显的名称,如 admin 或 administrator。

  5. 可选地,使用您自己的选择更改随机生成的密码。如果 WordPress 确定您的新密码较弱,您需要勾选出现的附加复选框以确认您想要使用弱密码。

  6. 在相应的字段中输入您的电子邮件地址(尽管在大多数本地开发安装中实际上不会发送电子邮件)。

  7. 如果您正在配置实时外部开发服务器,请勾选“阻止搜索引擎索引此站点”选项,因为我们不希望此开发站点出现在任何地方。

  8. 点击“安装 WordPress”以完成安装,您将自动登录到站点的 WordPress 仪表板。

  9. 在仪表板管理员栏中点击“开发站点”链接,以查看您的新站点:

图片

它是如何工作的...

在前几个步骤中,使用 phpMyAdmin 界面在本地 MySQL 服务器上创建数据库。这个基于 Web 的数据库管理工具与 XAMPP 和其他大多数 Web 服务器捆绑在一起。http://localhost/phpmyadmin地址将始终带您到数据库管理工具,即使您已根据前一个食谱中的说明移动了 Web 服务器的文档根目录。

一旦创建数据库并将 WordPress 文件复制到正确的位置,将浏览器指向本地 Web 服务器,它将搜索文档根目录以查找要发送回浏览器的 HTML 文件或要执行的 PHP 文件。在 WordPress 的情况下,Web 服务器找到index.php文件,并使用其 PHP 解释器执行它。当 WordPress 代码执行时,它会检查是否存在配置文件,如果找不到,则会启动安装过程。WordPress 代码在我们运行的本地 Web 服务器和任何在线可访问的远程实时 Web 服务器之间看不到任何区别。

虽然我们在安装过程中指定了管理员的电子邮件地址,但许多本地 Web 服务器未配置为发送电子邮件消息,因此在这些情况下我们永远不会收到任何电子邮件通信。在插件开发和测试电子邮件功能时,使用远程服务器更为可取。

一旦完成这个菜谱,你将拥有一个功能齐全的 WordPress 安装。

创建本地 Subversion 仓库

版本控制是任何代码开发项目的重要组成部分,用于跟踪项目的历史,拥有完整和有序的备份,以及能够轻松回滚更改以恢复到已知的工作状态。版本控制也是团队环境中开发项目时共享代码和其他文件的最佳和最高效的方式。除了是一个易于使用和配置的出色版本控制系统外,Subversion(通常称为 SVN)还是管理官方 WordPress 插件目录上所有提交的技术。因此,在初始插件开发过程中设置并使用本地 Subversion 仓库,你将立即准备好与社区分享你的创作。

如何操作...

  1. 访问 TortoiseSVN 网站 (tortoisesvn.net/downloads.html) 并下载适用于你 Windows 版本(32 位或 64 位)的免费 Subversion 客户端。

虽然这个菜谱主要关注在 Windows 平台上创建本地仓库,但在菜谱步骤之后,在 还有更多... 部分讨论了其他平台的等效工具。

  1. 启动 TortoiseSVN 安装程序,并使用所有默认安装选项进行安装。

  2. 在你的硬盘上创建一个新的文件夹,该文件夹将托管本地 Subversion 仓库(例如,c:\WPSVN)。

  3. 右键单击新文件夹,选择 TortoiseSVN | 在此处创建仓库菜单项,然后点击启动 Repobrowser。TortoiseSVN 将在目标目录中创建所需的文件结构,并显示仓库的内容,目前为空:

图片

工作原理...

Subversion 是一个免费的开源版本控制系统,旨在在项目开发过程中保持文件修订的有序和备份,并提供在任何时候访问所有文件的旧版本。如果你曾经发现自己复制电脑上的目录,并为每个副本赋予连续编号的名称或添加日期到它们的名称,那么你会认识到版本控制实际上只是实现相同目标的更组织和高效的方法,即保持已知工作版本的代码文件的备份,并能够访问文件的任何旧版本。

虽然默认的 Subversion 界面是一套命令行工具,但 TortoiseSVN 和其他许多客户端应用程序提供了图形工具来创建、访问和管理本地和远程仓库。

除了熟悉这个系统以便以后在wordpress.org上使用外,使用本地 Subversion 仓库将确保您始终可以轻松访问旧版本的插件,以防您执行的代码更改破坏了您的工作,并且您无法找出如何恢复到工作状态。

更多...

虽然网上有许多 Subversion 客户端可以与仓库交互,但并非所有客户端都包含创建仓库所需的必要管理工具,正如本指南中所示。您在寻找非 Windows 平台的 Subversion 客户端时,应寻找这些管理功能。

在 macOS X 上,versions([versionsapp.com/](http://versionsapp.com/))和 Cornerstone(https://www.zennaware.com/cornerstone)提供类似的功能,但都是付费应用程序。

在 Linux 上,PagaVCS 是免费的 TortoiseSVN 克隆版([code.google.com/p/pagavcs/](https://code.google.com/p/pagavcs/)),而 SmartSVN(http://smartsvn.com)是一个付费的 SVN 客户端。

手动创建仓库

如果您的 Subversion 客户端不提供创建本地仓库的功能,您可以从官方 Subversion 网站(subversion.apache.org/packages.html)下载 Subversion 命令行工具,并按照在线 Subversion 参考手册([svnbook.red-bean.com/](http://svnbook.red-bean.com/))中的说明手动创建仓库。

其他版本控制系统

虽然 Subversion 易于学习,并且是 WordPress 在其官方插件仓库中使用的系统,但其他版本控制系统,如 Git([https://git-scm.com/](https://git-scm.com/))和 Mercurial([https://mercurial-scm.org/](https://mercurial-scm.org/)),在开源开发社区中越来越受欢迎,也可以考虑用于管理您的插件代码。

参见

  • 将初始文件导入本地 Subversion 仓库指南

将初始文件导入本地 Subversion 仓库

一旦您已经设置好本地仓库,本指南将描述添加文件并开始跟踪其随时间变化的修订版本的步骤。为了在无需担心逐个将它们添加到仓库的情况下,灵活地创建多个插件,正如本指南中讨论的那样,我们将把整个 WordPress 插件目录添加到您的本地仓库中。

准备工作

您应该已经在您的计算机上安装了 Subversion 客户端并创建了本地仓库,如“创建本地 Subversion 仓库”食谱中所述。这些步骤将根据您选择的 Subversion 客户端和操作系统略有不同。

如何操作...

  1. 使用文件资源管理器导航到您的本地 WordPress 安装的 wp-content/plugins 目录(例如,如果遵循了前面的食谱,则为 c:\WPDev\wp-content\plugins)。

  2. 右键单击文件夹并选择 TortoiseSVN | 导入菜单项。

  3. 在仓库 URL 字段中输入您的本地 Subversion 仓库的文件位置(例如,file:///c:/WPSVN),如果尚未指定。

  4. 在导入消息字段中写一条消息,概述将要导入到仓库中的文件:

  1. 点击“确定”按钮以完成导入过程。

一旦开始导入操作,TortoiseSVN 将所有选定的文件发送到仓库,并在过程中显示每个文件的名称。导入操作结束时,它还会显示分配给这组第一个文件的修订号。

工作原理...

使用导入 Subversion 功能将所有选定的文件复制到仓库中。除了存储文件本身外,Subversion 还使用修订号和导入消息标识每个文件。修订号由 Subversion 生成,并在添加一组文件时递增。当搜索文件历史时特别有用。

导入消息由用户指定,实际上是可选的。尽管如此,当向仓库添加文件时设置有意义的导入消息很重要,因为它将使您在未来的搜索中更容易识别这些文件,了解它们的状态以及它们被添加到仓库的原因。

虽然这些步骤已经导致成功导入,您可能想知道为什么插件目录中没有发生变化。原因是导入过程仅将选定的文件复制到 Subversion 仓库中。还需要一个额外的步骤,称为检出过程,以开始跟踪更改和文件历史。

相关内容

  • “从 Subversion 仓库检出文件”食谱

从 Subversion 仓库检出文件

在将文件初步导入到 Subversion 仓库之后,需要将这些文件检出才能真正开始在版本控制环境中工作。本食谱解释了如何从您的本地仓库检出文件以及结果文件结构将发生哪些变化。

准备工作

在遵循此食谱之前,您应该已经安装了 Subversion 客户端,创建了本地仓库,并导入了文件。这些步骤将根据您选择的 Subversion 客户端和所使用的操作系统略有不同。

如何做到这一点...

  1. 如果你还没有在文件资源管理器中导航到本地安装的 WordPress 插件目录,请导航到该目录。

  2. 在目录窗口的空白处右键单击,并选择“SVN 检出...”菜单项。

  3. 如果尚未指定,请在仓库 URL 字段中输入本地 Subversion 仓库的文件位置(例如,file:///c:/WPSVN)。

  4. 将检出目录设置为本地 WordPress 安装的插件文件夹(例如,C:\WPDev\wp-content\plugins)。

默认情况下,TortoiseSVN 客户端在执行检出时会在路径末尾添加单词WPSVN。请确保删除路径的最后一部分,以便所有检出的文件都进入正确的位置。

  1. 在询问是否应将文件检入非空文件夹的对话框中点击“是”。此时,TortoiseSVN 将检索所有添加到仓库的文件并将它们复制到本地。

  2. 一旦操作完成,请查看plugins目录中的文件列表,以查看它是否已从其先前状态发生变化:

它是如何工作的...

执行检出操作会从仓库复制所有文件并将它们放置在目标目录中。它还在文件层次结构的顶级创建一个.svn目录来存储支持版本控制功能的文件。

默认情况下,大多数操作系统不会显示名称以点开头的文件夹,因为这通常表示隐藏文件和目录。要在 Windows 10 平台上显示隐藏文件夹,请执行以下步骤:

  1. 打开 Windows 资源管理器。

  2. 点击“查看”选项卡,并勾选名为“隐藏项”的选项。

.svn目录包含与当前文件夹中文件相关联的仓库地址信息。它还包含每个已检出文件的原始版本。这些原始文件用于 Subversion 确定每个文件相对于检出或更新时的状态何时发生变化。虽然当我们的仓库是本地托管时,.svn文件夹中所有文件的原始副本可能看起来有点冗余,但这种功能允许 Subversion 在远程仓库(如官方 WordPress 插件服务器)上工作时识别文件更改,即使你的计算机未连接到互联网。

还有更多...

当你使用 Subversion 和 TortoiseSVN 工作时,你创建、修改和删除的文件将经历多个不同的状态。以下部分解释了每个状态代表什么。

Subversion 文件状态

在每个文件图标上显示的绿色勾选标记指示器,在执行此配方后,表明我们的文件和目录自上次签出或更新以来没有被修改。随着我们开始修改现有文件和创建新文件,这些指示器会随时间而改变。以下是在您对一个项目进行工作时,文件可能具有的最常见状态列表,以及它们相关的 TortoiseSVN 图标:

  • 正常(绿色勾选标记):文件或目录处于正常状态,自上次签出或更新以来没有发生变化。

  • 已修改(红色感叹号):文件或目录自上次签出或更新以来已被修改。

  • 非版本化(蓝色问号):文件或目录不在版本控制之下。

  • 已添加(蓝色加号):文件或目录是新的,并已标记为在下次提交操作中提交到仓库。

  • 已删除(红色叉号图标):目录已被删除,将在下一次提交操作中从仓库中移除。

  • 忽略(灰色禁止符号):此文件或目录永远不会被发送到仓库,Subversion 应停止检查更改。此状态对于将私人文件,如个人文档或待办事项列表,保留在插件相同的目录中但不上传到仓库并跟踪其历史记录非常有用。

  • 冲突(黄色感叹号):此图标出现在冲突情况下,通常当多个人在同一仓库上工作,并且多个用户对同一文件进行了更改时。虽然 Subversion 客户端通常会尝试合并这些更改以创建单个文件,但冲突状态表明系统无法自动合并这些更改。冲突文件需要手动合并,或者用户需要指示文件是否优先于当前存储在仓库中的版本。

参见

  • 将更改提交到 Subversion 仓库配方

将更改提交到 Subversion 仓库

在项目过程中,通常会创建、修改或删除插件文件。这些更改应定期传输到 Subversion 仓库,以便对项目中的所有文件进行适当的备份。一个好的做法是每天至少提交一次更改,在实现插件功能的具体里程碑达到时,进行更频繁的提交操作。

此配方指示如何管理文件创建、修改和删除操作,以保持一切井然有序并在 Subversion 仓库中镜像。

准备工作

在执行此配方中的步骤之前,您应该已经安装了 Subversion 客户端,创建了本地仓库,并导入了签出的文件。这些步骤将根据您选择的 Subversion 客户端和所使用的操作系统略有不同。

如何操作...

  1. 如果您还没有在文件资源管理器中导航到本地安装的 WordPress 插件目录,请导航到该目录。

  2. 在文本编辑器中打开 hello.php 文件。

  3. 在第 7 行编辑插件名称,将其从 Plugin Name: Hello Dolly 更改为 Plugin Name: Goodbye Dolly

  4. 保存并关闭文件。你现在应该会注意到修改过的文件在文件资源管理器中被一个红色感叹号图标标识,表示它已被修改。

  5. plugins 目录中创建一个名为 chapter1 的新文件夹。

  6. 右键点击新文件夹,选择 TortoiseSVN | 添加... 菜单项以打开添加对话框。

  7. 点击 OK 按钮,将文件排队以便在下次提交更改时添加到仓库。你将看到文件夹名称上方出现一个蓝色加号,表示它将在下一次代码提交时被添加到仓库中。

  8. 导航到 chapter1 目录并创建一个名为 example.txt 的新文本文件。

  9. 返回到 plugins 目录。

  10. 右键点击 index.php 文件,并选择 TortoiseSVN | 删除菜单项。选定的文件将被立即删除并从文件资源管理器中消失。

  11. plugins 目录的空白部分右键点击,并选择 SVN 提交... 菜单项。这一步将显示提交对话框,顶部区域用于写入详细说明正在提交的更改的消息,底部区域包含文件列表。注意,除了一个文件外,所有文件旁边都有勾选标记,因为它们已被 Subversion 客户端识别为已更改,或者已通过 TortoiseSVN 界面添加或删除。没有勾选标记的文件是创建但未标记为在下一个提交操作中包含到 TortoiseSVN 快捷菜单中的文本文件:

图片

  1. 在适当的字段中输入操作的原因。

  2. 右键点击 chapter1/example.txt 文件,并选择添加菜单项以将其添加到操作中。

  3. 点击 OK 按钮,将所有更改发送到 Subversion 仓库。

它是如何工作的...

使用存储在 .svn 文件夹中的本地数据,Subversion 客户端能够分析目录内容,并识别自上次检出或更新操作以来新创建的、已修改的或缺失的所有文件,然后生成这些更改的列表。

当执行提交操作时,新文件会被添加到仓库中,修改过的文件会被上传并存储在其前一个版本旁边,而删除的文件会被标记为不再属于当前项目版本。虽然这些行为可能看起来有些奇怪,但正是通过保留文件的前一个版本,甚至保留不再属于项目的文件,Subversion 才能让我们浏览整个项目的历史。

虽然使用 TortoiseSVN 菜单标记文件和目录以添加以及删除不再需要的项目是首选的,但在提交即将发生时也可以执行这些操作,正如我们在配方步骤中看到的。

更多...

在文件提交到仓库之前,许多程序员和开发者都想查看对修改文件的更改,尤其是在一个在提交代码更改之前促进同行评审的环境中。

查看修改文件的差异

通过在提交对话框中右键单击任何已修改的文件并选择“差异”菜单项,TortoiseSVN 客户端将显示其内置的文件差异查看器工具,突出显示仓库中文件的最后一个版本和当前版本之间的不同部分。这使用户可以一目了然地看到更改了什么,并确保没有意外修改代码。

将文件更新到最新仓库版本

如果你是你唯一提交文件到仓库的人,并且你在一台计算机上工作,那么你永远不会需要使用 SVN 更新菜单项。此功能旨在比较你的本地文件与仓库,并检查仓库中是否有新文件或新版本,这些文件或版本在本地不存在。然后,它将应用所有必要的更改到这些文件的本地版本。记住,如果你在一个团队环境中工作或跨多台计算机开发项目,请定期使用 TortoiseSVN 中的 SVN 更新选项。

回滚未提交的文件更改

在将文件提交到仓库之前,可以使用 TortoiseSVN 菜单中的“回滚”项撤销自上次签出、更新或提交以来对该文件所做的所有更改。如果你对代码进行了更改,导致其功能损坏,并希望恢复到一个已知的好状态,这可能会很有用。

查看文件历史记录

随着时间推移,文件的不同版本被提交到仓库中,Subversion 会跟踪这些文件的每个版本以及与每个提交操作相关的消息。从 TortoiseSVN 菜单可访问的“显示日志”工具允许您查看对一个或多个文件所做的更改的完整历史记录,使用差异查看器查看每个文件的先前版本和当前版本之间的更改,并轻松恢复这些文件的特定版本。

安装专门的代码编辑器/文本编辑器

大多数操作系统都提供内置的文本编辑器。虽然可以使用这样的简单工具创建 WordPress 插件,但强烈建议在您的计算机上安装专门的代码编辑器,以简化插件开发工作。

准备工作

当然,并非所有代码编辑器都相同。在选择代码编辑应用程序时,以下是一些你应该寻找的功能:

  • PHP 语法高亮

  • 完成 PHP 函数名

  • 能够同时搜索多个文件

  • 能够突出显示搜索关键字(词)或所选文本的所有实例

  • 行号

  • 能够调整编辑器文本大小或指定替换字体

  • 同时打开多个文件的可能性

以下编辑器包含大多数或所有这些关键功能。大多数是免费工具,但有些是付费应用程序:

在 Windows 平台:

在 Mac 平台:

在 Linux 平台:

跨平台:

本食谱解释了如何安装专门的代码编辑器,并展示了基本的编辑器操作。它提供了使用 Windows 的 Programmer's Notepad 的详细步骤。

如何操作...

  1. 下载之前列出的文本编辑器之一的安装包。

  2. 运行编辑器的安装程序并选择默认设置。

  3. 启动文本编辑器。

  4. 从您本地 WordPress 安装的plugin目录中打开hello.php文件。您会看到代码的不同部分根据代码类型以不同的颜色显示。

  5. 双击一个单词以选择它。您将在文件内容中看到该单词的其他任何实例都被突出显示:

  1. 选择视图 | 行号菜单项(或根据您选择的文本编辑器命名的类似项)以在编辑器中显示行号。

它是如何工作的...

代码编辑器内置了解析器,使它们能够识别代码中的注释、PHP 语言函数、文本字符串以及各种其他元素。在屏幕上对这些元素进行着色使得阅读代码变得容易得多,可以快速看到函数名拼写错误,或者快速识别注释。

在为 WordPress 开发插件时,另一个至关重要的功能是能够在编辑器中看到行号。这个功能非常有用,尤其是在 PHP 代码出现错误时,因为通常会在错误发生时显示文件名和代码行。在大多数代码编辑器中,开发者可以滚动到特定的行或在一个快速转到对话框中输入行号,立即跳转到该行。

第二章:插件框架基础

在本章中,我们将涵盖以下主题:

  • 创建插件文件和头文件

  • 使用插件操作将输出内容添加到页面头文件

  • 使用 WordPress 路径实用函数加载外部文件和图片

  • 使用插件过滤器修改网站生成器元标签

  • 使用插件过滤器在每个项目内容后添加文本

  • 使用插件过滤器在页面主体中插入链接统计跟踪代码

  • 故障排除编码错误和打印变量内容

  • 创建一个新的简单简短代码

  • 创建具有参数的新简短代码

  • 创建一个新的封装简短代码

  • 加载样式表以格式化插件输出

  • 使用面向对象的 PHP 编写插件

简介

从其最初的版本开始,WordPress 就始终被设计为一个非常开放的平台。这种开放性不仅体现在其开源许可和分发模式上,还体现在其开放的插件架构上,为开发者提供了向用户提供更丰富体验的能力。

虽然基本的 WordPress 安装提供了大量的功能,这些功能从一次发布到下一次发布都在不断扩展,但用户往往需要添加一个功能,使其成为完美的网站管理系统。这就是插件发挥作用的地方。它们可以通过增强或操作 WordPress 网站显示和行政任务的几乎任何方面来填补这一空白。

就像 WordPress 一样,插件是用 PHP 编程语言编写的,其结构类似于更传统的语言,如 C 和 C++。此代码存储在纯 ASCII 文本文件中,当请求显示页面时,在 Web 服务器上读取和执行。使插件在 WordPress 中具有如此强大功能的秘密成分是在平台源代码中包含回调机制,称为钩子。这些钩子有两种类型,称为动作钩子和过滤器钩子,分别允许插件向网站添加内容并在显示之前修改数据。无论是渲染网站的首页、单个文章还是管理页面,WordPress 都有数百个入口点,可以在其中执行自定义函数。

除了增强 WordPress 功能的能力之外,插件的一个附带好处是它们添加到网站上的大多数功能都与活动主题无关。因此,喜欢频繁更换主题的用户在切换时不必担心手动将自定义元素添加到新主题中。

本章解释了动作钩子和过滤器钩子之间的区别,并展示了如何使用它们编写一组插件,这些插件的功能将从添加信息到页面头文件到定义新的自定义简短代码。

创建插件文件和头文件

创建 WordPress 插件的第一步是在插件目录中创建一个 PHP 文件,并添加必要的信息以便系统识别。本配方向您展示了如何创建一个基本的 WordPress 插件文件,以及如何从管理界面中查看和激活这个新的扩展。

准备工作

您应该能够访问一个 WordPress 开发环境,无论是在您的本地计算机上还是在远程服务器上,您将能够加载您的新插件文件。

如何操作...

  1. 导航到您开发安装的 WordPress 插件目录(wp-content/plugins)。

  2. 在插件目录中创建一个名为ch2-plugin-header的新子目录。

  3. 导航到该目录,并创建一个名为ch2-plugin-header.php的新文本文件。

  4. 在文本编辑器中打开新文件,并添加以下文本:

<?php 
/* 
Plugin Name: Chapter 2 - Plugin Header 
Plugin URI: 
Description: Declares a plugin that will be visible in the WordPress admin interface
Version: 1.0 
Author: Yannick Lefebvre 
Author URI: http://ylefebvre.ca 
License: GPLv2 
*/

虽然在代码示例中描述文本显示在两行中,但在您的代码中应全部输入一行,以便在 WordPress 已安装插件列表中完全显示。

下载示例代码您可以从www.packtpub.com的账户下载您购买的所有 Packt 书籍的示例代码文件。如果您在其他地方购买了这本书,您可以访问www.packtpub.com/support并注册,以便将文件直接通过电子邮件发送给您。

  1. 保存并关闭新文件。

  2. 登录到您开发 WordPress 安装的管理页面。

  3. 在左侧导航菜单中单击“插件”以显示所有已安装插件的列表。您应该看到您的新插件列在 WordPress 预装的默认插件旁边:

  1. 通过单击其名称下的“激活”链接来启用插件。您会看到您的新插件背景颜色改变,以指示它已被激活,同时会显示一条消息,指定激活成功。

它是如何工作的...

插件文件可以直接位于wp-content/plugins目录中,或者位于此位置下的子目录中。当您在管理界面中访问已安装的插件列表时,WordPress 会扫描所有潜在的插件位置,寻找包含符合此配方中指定格式的注释的 PHP 文件。实际上,在这些目录中的任何一个目录中都可以有一个或多个包含插件头数据的 PHP 文件,并且它们中的每一个都会在插件列表中显示出来。

仔细查看我们输入到文件中的代码,插件文件的第一行是一个标记,用于标识将被 PHP 解释器分析和执行的 PHP 代码的开始。可选地,我们可以在文件末尾包含一个关闭 PHP 标记(?>)。然而,大多数 PHP 开发者省略了关闭标记,因为在此标记之后有任何空格都会导致解释器显示警告。

为了确保与大多数 WordPress 安装的兼容性,在您的插件代码中使用完整的<?php开标签语法而不是<?简写版本非常重要,因为并非所有 PHP 安装都配置为支持简写版本,许多用户也没有权限更改服务器上的此类配置。

第二行和最后一行指示,括号内的文本应被视为文本注释。最后,注释中的每一行都包含一个特定的标签,指示其后跟的信息类型。当找到此信息时,WordPress 将检索有关插件的数据并将其添加到列表中。

当插件被激活时,WordPress 会验证文件内容以确保其为有效的 PHP 代码。然后,它将在网站上渲染任何页面时执行此内容,无论该页面是面向前端的还是后端管理部分。因此,最好只在插件使用时激活它们,以避免网站速度减慢。

当然,到目前为止,我们的新插件并没有在我们的 WordPress 安装中添加或修改任何功能,因为它不包含真正的代码,但这仍然是一个重要的第一步。

参见

  • 在第一章,准备本地开发环境中的在您的计算机上安装 Web 服务器食谱

  • 在第一章,准备本地开发环境中的下载和配置本地 WordPress 安装食谱

使用插件动作向页面头部添加输出内容

插件通常执行的一个常见操作是为 WordPress 生成的面向访客的页面头部添加额外内容。本食谱将向您展示如何注册一个动作钩子函数以便能够添加此类附加内容。为了使这个例子更加具体,我们将使用许多人都用来获取良好页面浏览统计数据的 Google Analytics 页面头部 JavaScript 代码。

如何操作...

  1. 导航到您的开发安装的 WordPress 插件目录。

  2. 创建一个名为ch2-page-header-output的新目录。

  3. 导航到该目录并创建一个名为ch2-page-header-output.php的新文本文件。

  4. 在代码编辑器中打开新文件,并在插件文件顶部添加一个适当的头部,将插件命名为“第二章 - 页面头部输出”。

  5. 添加以下代码行以注册一个在 WordPress 渲染页面头部时将被调用的函数:

add_action( 'wp_head', 'ch2pho_page_header_output' );
  1. 添加以下代码段以提供ch2pho_page_header_output函数的实现:
function ch2pho_page_header_output() { ?>
    <script>
        (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;
        i[r]=i[r]||function(){
        (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();
        a=s.createElement(o),
        m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;
        m.parentNode.insertBefore(a,m)})(window,document,'script',
        'https://www.google-analytics.com/analytics.js','ga');

        ga('create', 'UA-0000000-0', 'auto');
        ga('send', 'pageview');
    </script> 
<?php }
  1. 保存并关闭插件文件。

  2. 登录到您的开发 WordPress 安装的管理页面。

  3. 点击左侧导航菜单中的“插件”。

  4. 激活您的新插件。

  5. 导航到您网站的首页,并使用浏览器中的“查看页面源代码”功能查看网站的 HTML 源代码。此函数的确切名称将根据您使用的浏览器略有不同。阅读页面源代码,我们新函数之间的双括号内包含的所有代码将在您网站的头文件中可见:

图片

如果您是从本书的数字版本中复制粘贴代码,您将丢失原始代码缩进,应在您的代码编辑器中更正。

它是如何工作的...

add_action函数用于将自定义插件代码关联到 WordPress 钩子中的两种类型之一,即动作钩子。正如本章引言中简要提到的,钩子是使插件在 WordPress 中成为可能的功能。动作钩子允许在公共页面或管理页面准备显示时执行额外的代码。此代码通常向网站添加内容或更改特定动作的执行方式。

在这个示例中,我们编写的第一行代码注册了一个名为ch2pho_page_header_output的函数,与名为wp_head的动作钩子相关联。这个动作是 WordPress 当前版本中超过 2,400 个动作钩子之一,它允许任何已注册的函数向页面头部输出额外的内容。由于所有回声的内容都将显示,我们可以通过在 Google Analytics 代码周围放置?><?php标签来非常简单地编写我们的回调函数。这将告诉 PHP 显示该函数体内所有内容,而不是将其解释。

如您可能已注意到,当前代码并不十分灵活,因为您需要将 Google Analytics 账户号硬编码到输出中才能使其正常工作。在第三章,“用户设置和管理页面”的创建,将提供一种配置此类信息的方法,使我们的插件更加灵活。

现在,为了完全理解其语法,让我们更仔细地查看完整的add_action函数:

add_action ( 'hook_name', 'your_function_name', [priority],
             [accepted_args] );

第一个参数,钩子名称,表示我们想要将自定义函数与之关联的 WordPress 钩子名称。此名称必须拼写准确;否则,我们的函数将不会被调用,也不会显示错误消息。

第二个参数是要调用的插件函数的名称,用于执行动作。此函数可以具有任何名称,唯一条件是这个名称必须足够独特,以避免与其他插件或 WordPress 核心代码中的函数名冲突。在这个示例中,函数名以代表插件名称的缩写开头,这使得它更加独特。

优先级参数是可选的,如方括号所示,默认值为 10。它表示此插件相对于其他钩入同一动作的插件函数的执行优先级,数字越小表示优先级越高。

任何插件都可以使用 add_action 函数注册一个或多个与动作钩子关联的函数。由于 WordPress 正在渲染网页,它会保留所有条目的队列并在适当的时候调用它们。值得注意的是,钩子机制也被 WordPress 本身使用,因为它会定期在其代码中调用 add_action 函数来注册在正确时间被调用的函数。如果你意识到你需要你的函数在注册相同钩子的其他插件之前或之后被调用,请更改优先级参数的值。

add_action 函数的最后一个参数 accepted_args 具有默认值 1,应该分配一个数字。它也应该只为一些特定的钩子设置不同的值,在这些钩子中应该向注册的函数传递多个参数。其中一些钩子将在后面的菜谱中介绍。

还有更多...

寻找合适的钩子来注册插件函数是 WordPress 插件开发的一个重要部分。幸运的是,有几种方法可以获取现有钩子的信息,并了解它们在 WordPress 页面生成过程中的调用时间。

动作钩子在线列表

WordPress Codex (codex.wordpress.org/) 和 WordPress 代码参考 (developer.wordpress.org/reference/) 是包含大量对用户和开发者都有用的信息的文档网站。当涉及到动作钩子时,Codex 包含有关最常用钩子的信息,有基本的描述说明它们如何使用,并可以在以下位置找到:codex.wordpress.org/Plugin_API/Action_Reference。话虽如此,这并不是一个完整的列表。

有许多第三方网站解析 WordPress 源代码并提供他们自己的钩子列表(例如,hookr.io)。虽然这些类型的原始列表中钩子没有如此优雅的文档,但它们确实提供了它们名称和它们在 WordPress 为访客和管理员生成页面时被调用的基本信息。这些细节足以根据你试图实现的功能找到钩子。

在 WordPress 源代码中搜索钩子

由于 WordPress 是开源的,另一种查找有关钩子信息的方法是直接在其代码中进行搜索。对于每个接受用户函数的动作钩子,您将看到对 do_action 函数的调用以执行所有已注册的项目。如所见,该函数接受两个或更多参数,其中第二个(或多个)参数是可选的:

do_action ( 'tag', [$arg] ); 

对于本菜谱中所示的示例,搜索 do_action( 'wp_head' ) 可以发现,它是当主题在其头部文件中调用 wp_head() 函数时唯一被调用的函数:

do_action( 'wp_head' ); 

参见

  • 创建插件文件和头部 菜谱

使用 WordPress 路径实用函数加载外部文件和图片

有时,插件需要引用存储在插件目录中的外部文件(例如,图片、JavaScript 或 jQuery 脚本文件)。由于用户可以自由重命名插件的文件夹,甚至可以将插件文件直接安装到 WordPress 插件目录中,因此任何外部文件的路径都必须根据实际的插件位置动态构建。幸运的是,存在许多实用函数来简化这项任务。在本菜谱中,我们将编写一个简单的插件,该插件将为网站头部添加一个指向插件目录中图像文件的 favicon 元标签。

如何做到...

  1. 导航到您开发安装的 WordPress 插件目录。

  2. 创建一个名为 ch2-favicon 的新目录。

  3. 使用像 getfavicon.org 这样的网络服务检索网站 favicon(例如,http://www.packtpub.com),并将其以其默认名称(favicon.ico)存储在插件目录中。

  4. 导航到插件目录并创建一个名为 ch2-favicon.php 的新文本文件。

  5. 在代码编辑器中打开新文件,并在插件文件顶部添加一个适当的头部,将插件命名为“第二章 - Favicon”。

  6. 添加以下代码行以注册一个在 WordPress 渲染页面头部时将被调用的函数:

add_action( 'wp_head', 'ch2fi_page_header_output' ); 
  1. 添加以下代码部分以提供 ch2fi_page_header_output 函数的实现:
function ch2fi_page_header_output() { 
    $site_icon_url = get_site_icon_url();
    if ( !empty( $site_icon_url ) ) {
        wp_site_icon();
    } else {
        $icon_url = plugins_url( 'favicon.ico', __FILE__ );
    ?>

    <link rel="shortcut icon" href="<?php echo $icon_url; ?>" />
    <?php }
}
  1. 保存并关闭插件文件。

  2. 登录到您开发 WordPress 安装的行政页面。

  3. 在左侧导航菜单中单击“插件”。

  4. 激活您的新插件。

  5. 导航到您网站的前页并刷新它,以查看您通过插件代码指定的图标文件现在出现在您浏览器地址栏、标题栏或导航标签中,具体取决于您首选的浏览器。以下截图显示了 favicon 文件在 Internet Explorer、Mozilla Firefox 和 Google Chrome 中的渲染方式,从上到下:

  1. 导航到您开发站点的仪表板,并在外观菜单下选择自定义子菜单。

  2. 在“网站标识”下,指定一个至少为 512 x 512 像素的正方形图像作为网站图标;然后,在自定义化器的顶部按下保存并发布按钮。

  3. 刷新你的网站以查看新分配的网站图标现在已显示而不是favicon.ico文件。

它是如何工作的...

plugins_url实用函数,与__FILE__PHP 常量和我们的 favicon 文件名一起使用,使我们能够快速获取插件文件所在目录的 URL,并打印出适当的 HTML 命令以通知浏览器此文件的位置:

plugins_url( $path, $plugin ); 

plugins_url函数可以带参数或不带参数调用。在第一种情况下,它通过将第一个参数中找到的路径附加到第二个参数中指定的文件位置来构建一个 URL。在第二种情况下,它仅返回插件目录的位置。

在我们显示插件的自定义 favicon 文件之前,我们还检查用户是否已经使用 WordPress 自定义器分配了一个网站图标。如果是这样,我们优先考虑该图标,并使用内置的wp_site_icon函数显示它。

还有更多...

plugins_url函数是许多可以在插件中使用以帮助找到 WordPress 安装中文件位置的函数之一。其他有用的函数包括:

  • get_theme_root(): 返回主题安装目录的地址

  • get_template_directory_uri(): 获取当前主题文件的 URI

  • admin_url(): 提供 WordPress 管理页面的地址

  • content_url(): 指示wp-content目录的位置

  • site_url()home_url(): 返回网站地址

  • includes_url(): 提供 WordPress include文件的位置

  • wp_upload_dir(): 指示用户上传文件存储的目录

参见

  • 创建插件文件和标题菜谱

  • 使用插件动作向页面标题添加输出内容菜谱

使用插件过滤器修改网站生成器元标签

除了向网站添加功能或内容之外,插件通常执行的其他主要任务是增强、修改或减少在屏幕上显示之前的信息。这是通过使用 WordPress 过滤器钩子来完成的,它允许插件通过 WordPress API 注册一个自定义函数,因为内容是在发送到浏览器之前准备的。在这个菜谱中,你将学习如何实现你的第一个过滤器回调函数来修改作为网站标题一部分输出的生成器元标签的内容。

如何做到...

  1. 导航到你的开发安装的 WordPress 插件目录。

  2. 创建一个名为ch2-generator-filter的新目录。

  3. 导航到该目录并创建一个名为ch2-generator-filter.php的新文本文件。

  4. 在代码编辑器中打开新文件,并在插件文件顶部添加一个适当的标题,将插件命名为“第二章 - 生成器过滤器”。

  5. 添加以下代码行以注册一个函数,该函数将在 WordPress 准备输出作为页面标题一部分的生成器元标签的数据时被调用:

add_filter( 'the_generator', 'ch2gf_generator_filter', 10, 2 ); 
  1. 将以下代码部分添加到为 ch2gf_generator_filter 函数提供实现:
function ch2gf_generator_filter ( $html, $type ) {
    if ( $type == 'xhtml' ) {
        $html = preg_replace( '("WordPress.*?")', 
                              '"Yannick Lefebvre"', $html );
    }
    return $html;
} 
  1. 保存并关闭插件文件。

  2. 登录到您开发 WordPress 安装的管理页面。

  3. 在左侧导航菜单中单击“插件”。

  4. 激活您的新插件。

  5. 使用网络浏览器访问您的网站并显示页面源代码。搜索关键字 generator 将揭示内容生成器元标签已被修改,现在读取为:

<meta name="generator" content="Yannick Lefebvre" />

它是如何工作的...

add_filter 函数用于将自定义插件函数关联到第二种 WordPress 钩子,即过滤器钩子。过滤器钩子给插件提供了在 WordPress 执行过程中增加、修改、删除或完全替换信息的机会。为了启用此功能,过滤器函数会接收到可以作为函数参数修改的数据。一旦完成更改,它们必须将修改后的数据集返回给 WordPress。

与动作钩子不同,过滤器函数不得输出任何文本或 HTML 代码,因为它们是在输出准备过程中执行的,这可能会导致输出在网站布局中出现意外的位置。相反,它们应该返回过滤后的数据。

仔细查看 add_filter 函数的参数,我们可以看到它与我们在前面的菜谱中看到的 add_action 函数非常相似:

add_filter( 'hook_name', 'your_function_name', [priority],
            [accepted_args] ); 

第一个参数,钩子名称,表示我们希望我们的自定义函数与之关联的 WordPress 钩子名称。这个名字必须拼写准确;否则,我们的函数将不会被调用,也不会显示任何错误信息。

第二个参数是调用以过滤数据的插件函数的名称。这个函数可以有任何名称,唯一条件是这个名称必须足够独特,以避免与其他插件或 WordPress 代码中的函数冲突。

priority 参数是可选的,如方括号所示,默认值为 10。它表示此插件相对于由 WordPress 加载的其他插件的执行优先级,数字越小表示优先级越高。

函数的最后一个参数 accepted_args 具有默认值 1,表示将发送到您的自定义过滤器函数的参数数量。只有在您使用将发送多个参数的过滤器时,才应将其设置为更高的值,如本菜谱中的 $html$type 参数所示。

还有更多...

除了演示如何更改网站生成器名称之外,此插件还展示了如何使用高级 PHP 函数执行实际的文本替换。我们还探讨了更多关于过滤器钩子的学习资源。

preg_replace 函数

preg_replace 函数是一个 PHP 函数,可以用于根据搜索模式在字符串内执行搜索和替换操作。我们使用此函数而不是更简单的 str_replace,因为我们想要找到并替换 WordPress 关键字及其相关的版本号,该版本号会随着每个版本而变化。

网上列表和 apply_filters 函数的过滤器钩子

类似于动作钩子,常用过滤器钩子的信息可以在 WordPress Codex (codex.wordpress.org/Plugin_API/Action_Reference) 或提供原始函数列表的网站上找到(例如,hookr.io)。

通过在 WordPress 代码中搜索 apply_filters 函数的出现,也可以了解过滤器钩子。如下代码所示,此函数具有可变数量的参数,第一个参数是过滤器钩子的名称,第二个参数表示注册的函数将能够修改的值,其余的可选参数包含在过滤器函数实现中可能有用的附加数据:

apply_filters( $tag, $value, $var ... );  

对于本配方中显示的示例,在 WordPress 代码中搜索 apply_filters( 'the_generator' 可以发现它是在 the_generator 模板函数中被调用的:

echo apply_filters( 'the_generator', 
                    get_the_generator( $type ), $type ); 

参见

  • 创建插件文件和头文件 的配方

使用插件过滤器在每个项目内容后添加文本

在对页面标题、生成器元标签和网站图标进行多次更改后,此配方通过在每个帖子或页面中添加链接来发挥更积极的作用,允许访客发送当前查看项目的链接。此功能是通过附加到页面和帖子内容的过滤器钩子实现的,允许我们的自定义函数将自定义输出代码附加到所有在屏幕上显示的条目。

如何操作...

  1. 导航到您开发安装的 WordPress 插件目录。

  2. 创建一个名为 ch2-email-page-link 的新目录。

  3. 导航到该目录并创建一个名为 ch2-email-page-link.php 的新文本文件。

  4. 在代码编辑器中打开新文件,并在插件文件顶部添加一个适当的头文件,将插件命名为 第二章 - 邮件页面链接

  5. 访问一个图标下载网站,例如 iconarchive.com,并将一个大小为(32 x 32 像素)的 PNG 格式电子邮件图标下载到新插件目录中,命名为 mailicon.png

  6. 添加以下代码行以注册一个在 WordPress 准备显示帖子或页面内容时将被调用的函数:

add_filter( 'the_content', 'ch2epl_email_page_filter' ); 
  1. 将以下代码段添加到提供 ch2epl_email_page_filter 函数实现的代码部分:
function ch2epl_email_page_filter ( $the_content ) { 

    // build url to mail message icon downloaded from
    // iconarchive.com 
    $mail_icon_url = plugins_url( 'mailicon.png', __FILE__ ); 

    // Set initial value of $new_content variable to previous
    // content 
    $new_content = $the_content; 

    // Append image with mailto link after content, including 
    // the item title and permanent URL 
    $new_content .= '<div class="email_link">';
    $new_content .= '<a href="mailto:someone@somewhere.com?';
    $new_content .= 'subject=Check out this interesting article ';
    $new_content .= 'entitled ' . get_the_title(); 
    $new_content .= '&body=Hi!%0A%0AI thought you would enjoy ';
    $new_content .= 'this article entitled '; 
    $new_content .= get_the_title() . '.%0A%0A' . get_permalink(); 
    $new_content .= '%0A%0AEnjoy!">';

    if ( !empty( $mail_icon_url ) ) {
        $new_content .= '<img alt="Email icon" ';
        $new_content .= ' src="img/>        $new_content .= $mail_icon_url. '" /></a>';
    } else {
        $new_content .= 'Email link to this article';
    }
    $new_content .= '</div>';

    // Return filtered content for display on the site 
    return $new_content; 
} 
  1. 保存并关闭插件文件。

  2. 登录到您开发 WordPress 安装的行政页面。

  3. 在左侧导航菜单中单击“插件”。

  4. 激活您的新插件。

  5. 访问您的网站,查看每个帖子末尾和页面末尾的新邮件图标。

  6. 点击其中一个邮件链接。您的默认邮件客户端将显示您正在阅读的项目信息。唯一需要更新的信息是收件人地址,访客可以快速发送电子邮件:

它是如何工作的...

与上一个菜谱类似,这个插件使用 add_filter 函数注册一个自定义函数,以便在 WordPress 准备在屏幕上显示的项目内容时调用。当过滤器函数被调用时,它执行的第一项操作是创建一个指向在菜谱中下载的电子邮件图标的 URL。然后,它继续通过附加显示 mailto 链接的 HTML 代码来修改原始内容。同样的技术可以用来创建指向流行的社交媒体和链接分享网站的链接,只需简单更改链接的语法。一旦新内容准备就绪,它就会被返回到 WordPress,以便发送给任何其他已注册的过滤器,并随后在网站上显示。

还有更多...

这个菜谱还介绍了一对有用的 WordPress 实用函数,用于获取当前项目的内联内容。

get_the_title 和 get_permalink 函数

虽然这两个函数主要在主题模板文件中看到,但它们也可以由插件使用,以便轻松访问正在处理的项目的内容。

更具体地说,这个菜谱中使用的两个实用函数如下:

  • get_the_title(): 这个函数让我们可以快速访问项目的标题

  • get_permalink(): 一个返回项目永久链接(一个始终与该帖子或页面关联的 URL,即使它不再在网站首页上显示)的函数

参见

  • 创建插件文件和头文件 的菜谱

  • 使用 WordPress 路径实用函数加载外部文件和图像 的菜谱

  • 使用插件过滤器修改网站生成器元标签 的菜谱

使用插件过滤器在页面主体中插入链接统计跟踪代码

在创建两个附加文本到现有内容的过滤器函数之后,这个菜谱展示了如何在屏幕上显示之前修改页面内容。更具体地说,以下插件将扩展之前创建的 Google Analytics 标头插件,并为所有包含在帖子或页面中的链接添加一个 JavaScript 函数,以跟踪它们被访客点击的时间。

准备工作

您应该已经遵循了 使用插件动作向页面标题添加输出内容 的菜谱,以便为本菜谱和生成的插件提供一个起点,并且生成的插件应该已经在您的开发网站上激活。或者,您可以从 Packt Publishing 网站下载该菜谱的结果代码(Chapter 2/ch2-page-header-output/ch2-page-header-output.php)(www.packtpub.com/support)。

如何操作...

  1. 导航到你的开发安装的 WordPress 插件目录中的ch2-page-header-output文件夹。

  2. 在文本编辑器中打开ch2-page-header-output.php文件。

  3. 在现有函数之后添加以下代码行,以注册一个在 WordPress 准备显示页面或帖子内容时将被调用的函数:

add_filter( 'the_content', 'ch2lfa_link_filter_analytics' ); 
  1. 将以下代码段添加到为ch2lfa_link_filter_analytics函数提供实现的区域:
function ch2lfa_link_filter_analytics ( $the_content ) { 
    $new_content = str_replace( 'href',  
        'onClick="recordOutboundLink( this );return false;" href'
        , $the_content ); 

    return $new_content; 
} 
  1. 在注册当 WordPress 渲染页面页脚时将被调用的函数后添加以下代码行:
add_action( 'wp_footer', 'ch2lfa_footer_analytics_code' ); 
  1. 将以下代码段添加到为ch2lfa_footer_analytics_code函数提供实现的区域:
function ch2lfa_footer_analytics_code() { ?> 

<script type="text/javascript"> 
    function recordOutboundLink( link ) {
        ga( 'send', 'event', 'Outbound Links', 'Click', 
            link.href, {
                'transport': 'beacon',
                'hitCallback': function() { 
                    document.location = link.href; 
                }
            } );
        }
</script> 

<?php } 
  1. 保存并关闭插件文件。

  2. 前往仪表板的“页面”部分并编辑主页(或任何其他页面)。将链接添加到内容中,指向你选择的任何位置。

  3. 在你的网络浏览器中刷新你的网站并导航到之前修改的页面。

  4. 打开页面的源视图并找到你添加的链接。你会看到链接标签有额外的onClick JavaScript 代码,当访客跟随它时将被调用:

图片

  1. 滚动到页面底部,查看添加到页面页脚的recordOutboundLink JavaScript 函数的实现。

它是如何工作的...

通过调用add_filter设置的 内容过滤器函数在所有帖子或页面渲染到浏览器之前接收整个内容,并允许对这一信息进行任何数量的更改。在这种情况下,我们使用 PHP str_replace函数来搜索字符串href的任何出现,这表示一个链接。当找到字符串时,它被替换为对 JavaScript 函数的调用以及原始的href标签。

为了使这个插件完整,它还需要提供一个 JavaScript recordOutboundLink函数的实现。这是通过使用wp_footer钩子注册一个自定义函数来完成的,该函数将在网站页脚输出额外的内容,包括函数代码。

该插件自动化了许多与使用 Google Analytics 跟踪网站使用数据相关的任务。

参见

  • 使用插件动作向页面标题添加输出内容的配方

  • 使用插件过滤器在每个项目内容后添加文本的配方

调试编码错误和打印变量内容

当你从本书的页面转录代码段或开始编写自己的插件时,你很可能需要调试你的代码或处理你的插件旨在操作的数据。这个配方展示了在创建一个将隐藏未登录用户导航菜单中的项目的插件时,识别和快速解决这些错误的基本技术。

如何操作...

  1. 导航到你的开发安装的 WordPress 插件目录。

  2. 创建一个名为 ch2-nav-menu-filter 的新目录。

  3. 导航到该目录并创建一个名为 ch2-nav-menu-filter.php 的新文本文件。

  4. 在代码编辑器中打开新文件,并在插件文件顶部添加一个适当的标题,将插件命名为 第二章 - 导航菜单过滤器

  5. 添加以下代码行以注册一个函数,当 WordPress 准备显示网站导航菜单的数据时将被调用:

add_filter( 'wp_nav_menu_objects', 'ch2nmf_new_nav_menu_items', 
            10, 2 ); 
  1. 添加以下代码部分以提供 ch2nmf_new_nav_menu_items 函数的实现。注意,在第一行的开头故意将单词 functio 错拼:
functio ch2nmf_new_nav_menu_items( $sorted_menu_items, $args ) { 
    print_r( $sorted_menu_items ); 
    return $sorted_menu_items; 
} 

如果您使用的是专用代码编辑器,您应该能够判断文本字体不是一个已识别的关键字,因为它不会被着色为 PHP 关键字。

  1. 保存插件文件并保持代码编辑器打开。

  2. 登录到您开发 WordPress 安装的管理页面。

  3. 在左侧导航菜单中点击“插件”。

  4. 激活您的新插件。

  5. WordPress 将显示一个致命错误消息,表明插件无法激活,因为发现了一个语法错误。它还指出了错误发生的确切文件名和行号,有助于缩小问题发生的位置:

图片

  1. 返回代码编辑器,更正单词 function 的拼写,并保存文件。

  2. 第二次激活插件。现在它应该可以正确激活。

  3. 在代码编辑器中,删除单词 kbd>function 的最后一个字母以重新引入一个语法错误。

  4. 访问您的网站。您现在会看到整个网站已经消失,您的浏览器只显示一个空白页,并带有类似于我们刚才看到的错误消息。根据您的 Web 服务器配置,您也可能只得到一个空白页。

  5. 再次更正拼写错误,您的网站将恢复正常。您还会看到在导航菜单之前打印出大量信息。这些输出是由 print_r 函数生成的,旨在帮助我们了解我们的过滤器函数接收到的数据是如何组织的。一旦我们对这些数据有了良好的理解,我们将在下一步中能够正确地更改这些信息。

  6. 在 WordPress 仪表板中,导航到外观 | 菜单项,并在您的菜单中创建一个额外的自定义链接项,设置 URL 为 /privatearea,链接文本为 Private Area

图片

  1. 点击“保存菜单”按钮以存储所有更新。

  2. 在代码编辑器中,将过滤器函数内的 print_r 函数调用替换为以下代码:

// Check if used is logged in, continue if not logged 
if ( is_user_logged_in() == FALSE ) { 
    // Loop through all menu items received 
    // Place each item's key in $key variable 
    foreach ( $sorted_menu_items as $key => $sorted_menu_item ) { 
        // Check if menu item title matches search string 
        if ( 'Private Area' == $sorted_menu_item->title ) { 
            // Remove item from menu array if found using 
            // item key 
            unset( $sorted_menu_items[ $key ] ); 
        } 
    } 
} 
  1. 刷新您的网站,您将看到大型数组打印输出已消失。如果您以管理员身份登录,您还会在菜单中注意到私有区域链接。注销或使用另一个浏览器(您未登录到开发站点)以隐藏菜单项。

它是如何工作的...

当 WordPress 组装一个所有可用插件的列表以在管理界面中显示时,它并不会检查每个插件的 PHP 代码是否有效。实际上,这个检查是在插件激活时进行的。那时,任何语法错误都会立即被发现,新激活的插件将保持未激活状态,从而防止整个网站出现故障。

话虽如此,一旦插件被激活,其代码在 WordPress 渲染网页时每次都会被评估,并且任何随后保存到插件文件中的代码错误都会导致网站无法正确工作。因此,强烈建议设置一个本地开发环境,如第一章中所述的准备本地开发环境,以避免在插件代码中不可避免地出现错误时影响实时网站。在实时网站上,为了避免潜在的故障,一个更安全的方法是在修改插件之前将其停用,一旦修改完成,再重新激活它们,以确保在启用功能之前它们被重新验证。需要注意的是,使用这种方法,在您进行更改时,插件的功能将不可用,因此这不是修改已部署代码的最佳方式。

一旦代码运行正确,本食谱的第二部分将展示如何可视化自定义过滤器函数接收到的信息。虽然 WordPress Codex 网站提供了关于大多数过滤器目的的出色文档,但它并没有详细介绍发送到每个过滤器函数的信息结构。幸运的是,PHP 的print_r函数非常有用,因为它可以显示任何变量的内容,无论变量接收到的参数中存储了什么信息。

最后,但同样重要的是,自定义过滤器功能的实现使用 WordPress API 函数is_user_logged_in()来查看查看网站的人是否提供了登录凭据,然后继续解析所有菜单项,如果访客未登录,则删除“私有区域”菜单项。

还有更多...

除了本食谱中使用的调试技术外,WordPress 还提供了一些内置工具来帮助插件故障排除。

内置的 WordPress 调试功能

虽然wp-config.php文件位于 WordPress 文件结构的顶部,主要用于存储基本网站配置数据,但它也可以用来触发一系列调试功能。这些功能中的第一个是调试模式,它将在网站页面的顶部显示所有 PHP 错误、警告和通知。例如,激活此选项将显示您在代码中尝试访问的所有未定义变量,以及任何已弃用的 WordPress 函数。要激活此工具,请将定义WP_DEBUG常量的行的第二个参数从false更改为truewp_config.php中:

define( 'WP_DEBUG', true ); 

为了防止调试消息影响网站的布局,您可以下载一个有用的插件,称为 Debug Bar (wordpress.org/plugins/debug-bar/),以收集消息并在管理栏中显示:

可以从 wp-config.php 文件激活的其他调试功能如下:

  • WP_DEBUG_LOG: 将所有调试消息存储在站点 wp-content 目录下名为 debug.log 的文件中,以供以后分析

  • WP_DEBUG_DISPLAY: 表示是否应在屏幕上显示错误消息

  • SAVEQUERIES: 将数据库查询存储在变量中,可以在页面页脚中显示(有关更多信息,请参阅 codex.wordpress.org/Editing_wp-config.php#Save_queries_for_analysis

参见

  • 使用插件过滤器修改网站生成器元标签的食谱

创建一个新的简单短代码

短代码是 WordPress 中一个非常流行的工具,允许用户轻松地将插件或主题生成的内容添加到任何页面或帖子中,而无需熟悉 PHP 代码和编辑主题模板文件。由于它们非常简单易创建,短代码还可以用于轻松自动化重复包含在您网站内容中的内容的输出。

此食谱解释了如何创建一个新自定义短代码,该短代码将用于在任意帖子或页面中快速添加指向 Twitter 页面的链接,自动化重复性任务。

如何操作...

  1. 导航到您的开发安装的 WordPress 插件目录。

  2. 创建一个名为 ch2-twitter-shortcode 的新目录。

  3. 导航到该目录并创建一个名为 ch2-twitter-shortcode.php 的新文本文件。

  4. 在代码编辑器中打开新文件,并在插件文件顶部添加一个适当的标题,将插件命名为 第二章 - Twitter 短语

  5. 将以下代码行添加到声明新短语的声明中,只需使用两个字符 tl,并指定当代码在帖子或页面中遇到时应调用的函数名称:

add_shortcode( 'tl', 'ch2ts_twitter_link_shortcode' ); 
  1. 将以下代码段添加到为 ch2ts_twitter_link_shortcode 函数提供实现的代码中:
function ch2ts_twitter_link_shortcode( $atts ) {
    $output = '<a href="https://twitter.com/ylefebvre">';
    $output .= 'Twitter Feed</a>';
    return $output;
} 
  1. 保存并关闭插件文件。

  2. 登录到您的开发 WordPress 安装的行政页面。

  3. 在左侧导航菜单中单击插件。

  4. 激活您的新插件。

  5. 编辑您网站上现有的帖子,并在代码编辑器中使用短代码 [tl]:

  1. 保存并查看帖子,以查看短代码是否被替换为附加在“Twitter Feed”一词上的 Twitter 页面链接。

它是如何工作的...

短代码与动作钩子和过滤器钩子都有相似之处,因为当需要执行任务时,它们关联的自定义函数会被调用,就像动作钩子一样,但它们必须通过返回值返回输出,就像过滤器钩子一样。在外部数据方面,与某些类型的代码相关联的短代码函数将接收数据,而在其他情况下,它只会产生输出。

当在帖子或页面的文本中使用时,任何被一对方括号包围的短代码都会被 WordPress 引擎识别,然后它会搜索为该特定代码注册的函数。如果找到,则调用关联的函数,并使用预期的结果替换项目内容中的原始短代码文本。就像过滤器函数一样,短代码函数不得直接输出任何文本,因为这可能会在页面布局中出现意外的位置,因为 WordPress 在显示项目正文之前会调用所有短代码处理函数。

对于简单的短代码,例如本食谱中的那些,与它们关联的插件函数必须返回信息,但它们不会通过函数参数接收任何额外的数据。换句话说,它们可以依赖实用函数,如get_the_IDget_the_title和其他 WordPress 实用函数,以便能够生成适当的输出。在后面的食谱中看到的其他类型的短代码将具有更多的上下文和配置选项。短代码还可以访问存储的选项数据,这将在第三章,用户设置和管理页面中介绍。

参见

  • 创建插件文件和头部食谱

创建带参数的新短代码

虽然简单的短代码已经通过在帖子编辑器中输入几个字符提供了将复杂内容输出到页面的潜力,但当它们与传递给其关联处理函数的参数结合使用时,短代码变得更加有用。使用这种技术,创建一个短代码来加速在 WordPress 帖子或页面中插入外部内容变得非常容易,只需指定短代码和要显示的源元素的唯一标识符即可。

我们将通过创建一个短代码来展示这个概念,这个短代码将被用来快速将 Twitter 动态添加到帖子或页面中。

如何实现...

  1. 导航到您开发安装的 WordPress 插件目录。

  2. 创建一个名为ch2-twitter-embed的新目录。

  3. 导航到这个目录并创建一个名为ch2-twitter-embed.php的新文本文件。

  4. 在代码编辑器中打开新文件,并在插件文件顶部添加一个适当的头部,将插件命名为Chapter 2 - Twitter Embed

  5. 添加以下代码行以声明一个新的短代码并指定当短代码在帖子或页面中找到时应调用的函数名称:

add_shortcode( 'twitterfeed', 'ch2te_twitter_embed_shortcode' );
  1. 将以下代码部分添加到为ch2te_twitter_embed_shortcode函数提供实现:
function ch2te_twitter_embed_shortcode( $atts ) {
    extract( shortcode_atts( array(
        'user_name' => 'ylefebvre'
    ), $atts ) );

    if ( !empty( $user_name ) ) {
        $output = '<a class="twitter-timeline" href="'; 
        $output .= esc_url( 'https://twitter.com/' . $user_name );
        $output .= '">Tweets by ' . esc_html( $user_name );
        $output .= '</a><script async ';
        $output .= 'src="img/"';
        $output .= ' charset="utf-8"></script>';
    } else {
        $output = '';
    }
    return $output;
}
  1. 保存并关闭插件文件。

  2. 登录到您开发 WordPress 安装的管理页面。

  3. 在左侧导航菜单中单击“插件”。

  4. 激活您的新插件。

  5. 创建一个新页面,并在页面编辑器中使用短代码[twitterfeed user_name='WordPress'],其中WordPress是要显示的源 Twitter 用户名:

图片

  1. 发布并查看页面,以查看短代码已被嵌入到您网站上的 Twitter 源所取代。

  2. 编辑页面,删除user_name参数及其相关值,只留下核心的[twitterfeed]短代码在帖子中,然后更新以保存更改。

  3. 刷新页面,查看是否仍然显示该源,但现在显示的是来自另一个账户的推文。

它是如何工作的...

当短代码与参数一起使用时,这些额外的数据会被发送到$atts参数变量中关联的处理函数。通过使用标准的 PHP extract和 WordPress 特定的shortcode_atts函数的组合,我们的插件能够解析发送到短代码的数据,并创建一个标识符和值的数组,这些标识符和值随后被转换成 PHP 变量,我们可以在短代码实现函数的其余部分中使用。在这个特定的例子中,我们期望使用一个名为user_name的单个变量,它将被存储在名为$user_name的 PHP 变量中。如果用户没有提供任何参数就输入了短代码,将默认将ylefebvre分配给用户名变量,以确保插件仍然可以工作。由于我们将在代码中接受用户输入,我们还验证用户没有提供空字符串,并使用esc_htmlesc_url函数从输入字符串中删除任何可能有害的 HTML 字符,并确保链接目标 URL 是有效的。

一旦我们获得了 Twitter 用户名,我们就可以组合所需的 HTML 代码,将 Twitter 源嵌入我们的页面并显示所选用户的推文。

虽然这个例子只有一个参数,但可以为短代码定义多个参数。

参见

  • 创建一个新的简单短代码配方

创建一个新的封装短代码

WordPress 中还有另一种类型的短代码,它将内容封装在帖子或页面中。使用类似于 HTML 标签的语法,封装短代码可以用来标识需要以特殊方式处理的项的内容部分。例如,可以使用这种类型的短代码来设置帖子的部分样式。

作为创建包围短代码的示例,本食谱展示了如何创建一组标签,这些标签将标识帖子或页面中仅应向登录网站的访问者显示的部分。以这种方式,短代码的行为类似于过滤器钩子,额外的好处是您不需要解析这些标签的实例,这在过滤器中通常是必须做的。

如何操作...

  1. 导航到您的开发安装的 WordPress 插件目录。

  2. 创建一个名为 ch2-private-item-text 的新目录。

  3. 导航到该目录并创建一个名为 ch2-private-item-text.php 的新文本文件。

  4. 在代码编辑器中打开新文件,并在插件文件顶部添加一个适当的标题,命名为“第二章 - 私有项目文本”。

  5. 向声明新短代码并指定当短代码在帖子或页面中找到时应调用的函数的代码行添加以下代码:

add_shortcode( 'private', 'ch2pit_private_shortcode' ); 
  1. 向提供 ch2pit_private_shortcode 函数实现的代码部分添加以下代码:
function ch2pit_private_shortcode( $atts, $content = null ) { 
    if ( is_user_logged_in() ) {
        return '<div class="private">' . $content . '</div>'; 
    } else {
        $output = '<div class="register">';
        $output .= 'You need to become a member to access ';
        $output .= 'this content.</div>';
        return $output;
    }
} 
  1. 保存并关闭插件文件。

  2. 登录到您的开发 WordPress 安装的管理页面。

  3. 在左侧导航菜单中点击“插件”。

  4. 激活您的新插件。

  5. 创建一个新帖子,并用 [private] 和 [/private] 标签包裹一些内容:

  1. 保存并查看帖子,以查看您登录到您的网站时文本是可见的。

  2. 登出并刷新页面,以查看包围的文本已被通用消息替换。

它是如何工作的...

类似于过滤器函数,包围的短代码会接收到用新标签包裹的文本的副本。然后可以返回带有附加 HTML 代码的此文本,或者完全用新内容替换它。在这个特定的情况下,我们使用了 WordPress 的is_user_logged_in函数来确定当前访问者是否登录到网站。根据查询结果,代码确定原始内容是否应该显示一些额外的样式代码,或者访问者应该看到一条鼓励他们加入网站的消息。

相关内容

  • 创建一个新的简单短代码 食谱

加载样式表以格式化插件输出

当插件向帖子或页面的现有内容添加自定义内容或插入样式标签时,就像在之前展示如何创建包围短代码的食谱中所做的那样,它通常需要加载一个自定义样式表来格式化这些新元素。本食谱展示了如何在 WordPress 样式队列中添加样式表以格式化之前食谱中创建的私有输出。此队列在页面头部渲染时进行处理,列出所有需要加载以正确显示网站的样式表。

准备工作

您应该已经遵循了 创建一个新的封装短代码 菜单,以便为本菜谱提供一个起点,并且生成的插件应该仍然在您的开发站点中处于活动状态。或者,您可以从 Packt 网站(www.packtpub.com/support)下载该菜谱的结果代码(Chapter 2/ch2-private-item-text/ch2-private-item-text.php)。

如何操作...

  1. 导航到您开发安装的 WordPress 插件目录中的 ch2-private-item-text 文件夹。

  2. 在文本编辑器中打开 ch2-private-item-text.php 文件。

  3. 在现有函数之后添加以下代码行,以注册一个将在 WordPress 页面显示过程开始时被调用的函数:

add_action( 'wp_enqueue_scripts', 'ch2pit_queue_stylesheet' ); 
  1. 将以下代码部分添加到提供 ch2pit_queue_stylesheet 函数实现的代码段:
function ch2pit_queue_stylesheet() { 
    wp_enqueue_style( 'privateshortcodestyle',
                      plugins_url( 'stylesheet.css', __FILE__ ) ); 
} 
  1. 保存并关闭插件文件。

  2. 在插件的目录中创建一个新的文本文件,命名为 stylesheet.css,并在代码编辑器中打开它。

  3. 将以下内容添加到文件中:

.private {  
    color: #6E6A6B; 
}

.register { 
    background-color: #ff4d4d;
    color: #fff;
    padding-left: 10px;
} 
  1. 保存并关闭文本文件。

  2. 导航到您的网站,确保您已登录,并刷新包含私有文本内容的页面。您应该注意到文本现在以灰色显示。

  3. 从网站上注销并刷新页面,以查看注册消息样式也已更改。

它是如何工作的...

虽然通过使用 wp_head 动作钩子注册函数直接编写 HTML 代码来加载 CSS 文件是可能的,就像我们之前做的那样,但 WordPress 有设计用来帮助避免在网站上加载重复样式表或脚本的实用函数。在这个特定示例中,使用 wp_enqueue_script 将插件样式表文件放入队列中,该队列将在插件头部渲染时处理,关联名称为 privateshortcodestyle。一旦 WordPress 处理完所有插件并将所有样式表请求缩减为单个实例,它将输出必要的 HTML 代码来加载所有这些样式表。

stylesheet.css 文件的内容是正常的 CSS 代码,指定将 private 类分配给任何文本应显示为灰色,而显示给未注册用户的文本应在红色背景上显示为白色。

参见

  • 创建一个新的封装短代码 菜单

使用面向对象的 PHP 编写插件

到目前为止,本章中涵盖的所有插件示例都是使用过程式 PHP 编程风格编写的,所有函数都直接声明在插件的主体中,钩子注册函数可以直接访问这些函数。

WordPress 也可以使用面向对象的 PHP 方法编写。本菜谱解释了如何将前一个菜谱中的代码转换为具有相同功能的基于类的版本。

准备工作

您应该已经遵循了加载样式表以格式化插件输出的配方,以便为本配方提供一个起点。或者,您可以从 Packt Publishing 网站(www.packtpub.com/support)下载该配方生成的代码(Chapter 2/ch2-private-item-text/ch2-private-item-text-v2.php)。

如何做到这一点...

  1. 登录到您的 WordPress 安装的管理页面。

  2. 点击左侧导航菜单中的插件。

  3. 检查Chapter 2 - Private Item Text插件是否当前处于激活状态,如果是,请将其停用。

  4. 复制ch2-private-item-text目录的全部内容,并将副本重命名为ch2-oo-private-item-text

  5. 导航到新命名的文件夹,并将主 PHP 代码文件重命名为ch2-oo-private-item-text.php

  6. 在代码编辑器中打开新命名的插件文件。

  7. 更新插件头部,将插件名称更改为Chapter 2 - Object-Oriented - Private Item Text

  8. 在插件头部之后立即添加以下文本以声明一个新的类,并指定该类的构造方法:

class CH2_OO_Private_Item_Text {      
    function __construct() { 
    }      
} 

$my_ch2_oo_private_item_text = new CH2_OO_Private_Item_Text(); 
  1. 将对add_shortcodeadd_action函数的调用移动到类构造函数内部。

  2. 修改add_shortcodeadd_action函数的第二个参数如下:

add_shortcode( 'private', array( $this, 
 'ch2pit_private_shortcode' ) ); 

add_action( 'wp_enqueue_scripts', array( $this, 
 'ch2pit_queue_stylesheet' ) ); 
  1. 将完整的ch2pit_private_shortcodech2pit_queue_stylesheet函数移动到类体内部(在构造方法之后和类结束括号之前)。

  2. 保存并关闭修改后的文件。

  3. 登录到您的开发 WordPress 安装的管理页面。

  4. 点击左侧导航菜单中的插件。

  5. 激活新插件。

  6. 访问您的网站,以查看私有项目内容功能仍然存在并且像以前一样工作。

它是如何工作的...

我们对插件应用的所有代码更改首先声明了一个用于我们插件所有功能的类,并包含了一个该类的构造方法。构造方法在类被实例化后立即调用,即插件代码中的最后一行,并且可以用来将自定义函数与所有动作钩子、过滤器钩子和短代码相关联。

使用面向对象方法的主要好处是,您在命名钩子回调和所有其他函数时不必那么小心,因为这些名称是局部于类的,并且可以与任何其他类或过程式 PHP 代码中声明的函数名称相同。

参见

  • 创建一个新的封装短代码配方

第三章:用户设置和管理页面

本章专注于设置页面,使用户能够配置插件设置。它涵盖了以下主题:

  • 在插件初始化时创建默认用户设置

  • 使用数组存储用户设置

  • 删除时移除插件数据

  • 在设置菜单中创建管理页面菜单项

  • 创建多级管理菜单

  • 添加指向外部页面的菜单项

  • 隐藏用户不应访问的默认菜单项

  • 使用 HTML 渲染管理页面内容

  • 处理和存储插件配置数据

  • 保存选项时显示确认消息

  • 添加自定义帮助页面

  • 使用设置 API 渲染管理页面内容

  • 从操作和过滤器钩子访问用户设置

  • 使用元框格式化管理页面

  • 将管理代码从主插件文件中分离出来以优化站点性能

  • 将样式表数据存储在用户设置中

  • 从单个管理页面管理多套用户设置

  • 创建具有管理页面的网络级插件

简介

正如我们在第二章“插件框架基础”中看到的,插件注册自定义函数与操作和过滤器钩子以改变或增强 WordPress 渲染网页的方式是非常容易的。尽管如此,第二章“插件框架基础”中的一些示例在处理自定义用户信息时存在局限性,例如无法轻松指定一个 Google Analytics 账户号码。

为了使插件对广泛用户群易于使用,通常很重要的一点是创建一个或多个管理页面,用户可以在其中提供特定于其安装的详细信息,输入外部账户信息,并自定义插件功能的一些方面。例如,默认 WordPress 安装中提供的 Akismet 插件提供了一个配置页面,可以在设置 | Akismet 配置菜单下找到。幸运的是,WordPress 有一套丰富的函数,允许插件开发者轻松地组合配置页面,使其与行政面板的其他部分无缝融合。

本章介绍了如何使用 WordPress 选项应用程序编程接口API)函数在站点数据库中存储和访问用户选项。然后继续解释如何创建自定义对话框,使用户能够完全控制你创建的插件的配置。

在插件初始化时创建默认用户设置

大多数用户可配置插件的典型第一步是在插件激活时为所有选项创建一组默认值。这些默认选项随后将被用于在站点管理员访问插件设置页面时填充插件设置页面。本菜谱展示了如何注册一个在插件激活时被调用的函数,以及如何将选项数据存储在站点数据库中。

如何操作...

  1. 导航到你的开发安装的 WordPress 插件目录。

  2. 创建一个名为 ch3-individual-options 的新目录。

  3. 导航到该目录并创建一个名为 ch3-individual-options.php 的新文本文件。

  4. 在代码编辑器中打开新文件,并在插件文件顶部添加一个适当的标题,将插件命名为 第三章 - 单个选项

  5. 添加以下代码行以注册一个在插件激活时执行的功能,无论是初始安装后还是升级后:

register_activation_hook( __FILE__, 'ch3io_set_default_options' );
  1. 将以下代码段添加到提供 ch3io_set_default_options 函数实现的区域:
function ch3io_set_default_options() {
    if ( false === get_option( 'ch3io_ga_account_name' ) ) {
        add_option( 'ch3io_ga_account_name', 'UA-0000000-0' );  
    }
}
  1. 保存并关闭插件文件。通过点击此 第三章 - 单个选项 插件的“激活”选项来执行刚刚添加的激活函数。

  2. 在你的 Web 服务器 MySQL 数据库管理工具中,选择你的 WordPress 数据库(如果你遵循了 第一章,准备本地开发环境中的说明,则为 wordpressdev),然后选择 wpdev_options 表。点击 SQL 选项卡,将默认查询替换为以下语句,然后点击“执行”:

select * from wpdev_options where option_name = 'ch3io_ga_account_name'
  1. 你的查询应返回一个包含分配给新选项的默认值的单行:

工作原理...

register_activation_hook 函数用于向 WordPress 指示在激活插件时应调用的函数名称。与其他钩子不同,此函数需要将主插件代码文件的名称作为其第一个参数发送,以及关联函数的名称。为了轻松做到这一点,我们可以利用 PHP 的 __FILE__ 常量作为第一个参数,这将解析为文件名。

当回调函数被调用时,我们可以使用选项 API 在网站的 MySQL 数据库的选项表中创建、更新或删除设置。在这个特定例子中,我们使用 add_option 函数轻松创建一个名为 ch3io_ga_account_name 的选项,其默认值为 UA-0000000-0

就像函数名称一样,在命名插件选项时应该小心,以避免与其他插件冲突。一个好的做法是在每个变量名开头添加一个唯一的前缀。

在调用创建新选项之前,激活函数会使用get_option函数检查选项是否存在于 WordPress 选项表中。如果返回值是 false,表示未找到该选项,则可以创建一个新的默认选项。任何其他结果都表明该插件之前已在网站上激活,并且选项可能已经从默认值更改。在编写此代码时,重要的是要记住,插件每次使用 WordPress 更新工具更新时都会被停用和重新激活,这会导致调用其激活函数。也有可能用户暂时停用插件以调试网站问题,然后在稍后时间将其恢复,这也可能导致激活函数被调用。

最后,需要注意的是,如果需要实现插件所需的功能而需要多个选项,则可以多次调用add_option函数。尽管如此,没有必要验证所有选项的存在,因为检查单个选项就足以表明它们之前已经被设置。

更多内容...

除了为插件创建默认值之外,激活钩子还可以用来执行更高级的任务,例如与自定义数据库表交互或进行数据初始化,这将在后续章节中看到。相比之下,类似的停用功能钩子在大多数插件的创建中并没有实际用途。

停用函数

与我们在本食谱中使用的激活函数类似,WordPress 提供了一种注册停用函数的方法(使用register_deactivation_hook)。虽然使用此函数删除插件创建的选项可能很有吸引力,但无法知道激活函数被调用的原因。可能触发此调用的三种情况是插件升级、暂时停用以调试网站问题或插件即将被删除。由于在第一种和第二种情况下最好保留用户选项,因此任何清理和数据删除代码应放置在插件卸载文件中,如后续食谱中所述。

参见

  • 删除插件数据时的操作食谱

使用数组存储用户设置

而在之前的食谱中,虽然很好地在网站的选项表中为每个单独的插件选项创建了条目,但管理用户设置的另一种方法是将其作为数组存储在数据库中。

此食谱创建与之前相同的选项,但还添加了第二个选项,并使用数组而不是单个选项来存储它们。它还结合了升级策略来处理插件随时间发展而创建的附加选项。

准备工作

您应该已经遵循了第二章中名为使用插件过滤器在页面主体中插入链接统计跟踪代码的食谱,以获得本食谱的起点。或者,您可以从 Packt 网站下载的代码包(www.packtpub.com/support)中获取结果代码(Chapter 2/ch2-page-header-output/ch2-page-header-output-v2.php),并将文件重命名为ch2-page-header-output.php

如何操作...

  1. 导航到您开发安装中 WordPress 插件目录的ch2-page-header-output文件夹。

  2. 在代码编辑器中打开文件ch2-page-header-output.php

  3. 添加以下代码行以注册在插件激活时调用的函数:

register_activation_hook( __FILE__, 
                          'ch2pho_set_default_options_array' );
  1. 添加以下代码段以提供ch2pho_set_default_options_array函数的实现:
function ch2pho_set_default_options_array() {
    ch2pho_get_options();
}
  1. 添加以下代码以提供ch2pho_get_options函数的实现:
function ch2pho_get_options() {
    $options = get_option( 'ch2pho_options', array() );

    $new_options['ga_account_name'] = 'UA-0000000-0';
    $new_options['track_outgoing_links'] = false;

    $merged_options = wp_parse_args( $options, $new_options );

    $compare_options = array_diff_key( $new_options, $options );
    if ( empty( $options ) || !empty( $compare_options ) ) {
        update_option( 'ch2pho_options', $merged_options );
    }
    return $merged_options;
}
  1. 保存并关闭插件文件。

  2. 前往管理界面的插件部分。

  3. 点击Chapter 2 - Page Header Output插件的“停用”链接,然后点击“激活”链接以执行刚刚添加的激活函数。

  4. 使用您的 Web 服务器 MySQL 数据库管理工具,查询 WordPress 安装中的wpdev_options表,查找名为ch2pho_options的选项:

select * from wpdev_options where option_name = 'ch2pho_options'
  1. 您的查询应返回一行,其中包含表示数组中所有字段的序列化数据集:

图片

它是如何工作的...

add_optionget_optionupdate_option函数接受单个变量或数据数组作为值。当给定一个数组时,它们将接收到的信息转换为序列化数组,该数组被存储在网站数据库中。使用数组而不是多个选项的主要优势是,所有信息都可以通过单个函数调用检索,从而优化对 MySQL 数据库的访问并简化您的插件代码。这对于您的插件选项需要在每次渲染网站页面时查询尤为重要。

当然,这个优势只有在您需要同时使用大多数插件选项时才是真实的。否则,您的代码将无端地管理大量数据。

使用数组而不是单个选项的另一个好处是,每个选项的名称可以更短、更简单,因为您只需要担心在顶级选项名称级别避免命名冲突,而不是数组的每个键。最后,将所有选项存储在单个数组中,使得批量删除这些选项比它们分别存储要容易得多,正如我们将在下一个食谱中看到的那样。

这个配方的大部分代码定义了一个名为ch2pho_get_options的实用函数,该函数用于确保我们在检索选项时始终得到良好的值,即使我们的插件第一次运行或通过升级引入了新的选项。作为这个实用函数的一部分,我们使用wp_parse_args函数快速比较由get_option函数检索到的现有选项集与由$new_options数组指定的当前插件默认选项集。对于在现有选项中找不到的每个数组元素,wp_parse_args将简单地将其合并到最终数组中,该数组在函数结束时返回。我们选项检索函数的最后部分检查前一个选项数组是否为空或是否向新数组中添加了任何新键,使用 PHP 的array_diff_key函数。在这两种情况下,它将保存更新后的选项数组回站点数据库。

最后需要注意的是,虽然ch2pho_get_options函数返回所有站点选项的数组,但我们实际上在这个配方中并没有使用这个返回值;我们将在本章后面的配方中使用它。

参见

  • 第二章中插件框架基础使用插件动作向页面标题添加输出内容配方

  • 第二章中插件框架基础在页面主体中使用插件过滤器插入链接统计跟踪代码配方

  • 删除时移除插件数据配方

删除时移除插件数据

就像任何软件一样,用户可能会决定从他们的 WordPress 安装中移除插件,如果他们不再需要它提供的功能,或者他们找到了他们更喜欢的替代方案。

当这种情况发生时,插件作者必须决定是否应该将存储在站点数据库中的所有配置数据保留在原位,以便将来更容易重新安装插件,或者移除所有这些信息,留下一个干净的数据库。

这个配方展示了如何创建一个卸载函数,该函数将从站点的数据库中移除选项数据。

准备工作

您应该已经遵循了使用数组存储用户设置的配方,以便为删除准备选项数据,并且生成的插件在您的开发站点中仍然处于活动状态。或者,您可以从下载的代码包中获取生成的代码(第三章/ch2-page-header-output/ch2-page-header-output-v3.php)。您应该将文件ch2-page-header-output-v3.php重命名为ch2-page-header-output.php,并在开始此配方之前激活插件一次。

如何操作...

  1. 导航到您的开发安装的 WordPress 插件目录中的ch2-page-header-output文件夹。

  2. 创建一个名为uninstall.php的新文件。

  3. 在文本编辑器中打开新文件,并添加以下代码:

<?php
// Check that code was called from WordPress with
// uninstallation constant declared
if ( !defined( 'WP_UNINSTALL_PLUGIN' ) ) {
    exit;
}

// Check if options exist and delete them if present
if ( false != get_option( 'ch2pho_options' ) ) {
    delete_option( 'ch2pho_options' );
}
  1. 保存并关闭插件文件。

  2. 导航到您开发 WordPress 安装的管理页面。

  3. 在左侧导航菜单中点击插件。

  4. 禁用第二章 - 页面标题输出插件。

  5. 在以下步骤中删除插件时,为了防止丢失,请复制您的插件和卸载文件。副本应移动到plugins文件夹之外,以避免 WordPress 看到两个插件副本。

  6. 第二章 - 页面标题输出插件下点击删除链接。

  7. 点击确定按钮以删除所有插件文件。

图片

根据您开发站点的配置,在 WordPress 能够删除插件文件之前,您可能需要提供 FTP 凭据。

  1. 使用您的 Web 服务器的 MySQL 数据库管理工具,查询您的 WordPress 安装中的wpdev_options表,查找名为ch2pho_options的选项,以查看该选项是否已被删除:
select * from wpdev_options where option_name = 'ch2pho_options'

它是如何工作的...

当插件处于非活动状态且网站管理员点击其删除链接时,WordPress 会检查插件目录中是否存在名为uninstall.php的文件。如果文件存在且用户点击确定按钮以删除,WordPress 将进行所有插件文件的删除并执行uninstall.php文件的内容。此文件应包含直接删除所有插件选项和插件代码创建的任何其他内容的 PHP 代码。一旦执行,卸载脚本将与文件一起被删除。

查看卸载脚本的内容,前几行代码检查 WordPress 在调用脚本之前是否设置了某个常量。如果不存在,出于安全考虑,脚本将立即中止。这确保了知道已安装某个插件的外部访客无法尝试删除它。一旦验证了意图,其余的代码将检查之前配方中创建的ch2pho_options数组是否存在,并将其删除。如果您创建了多个选项来存储配置数据,您将需要使用delete_option函数的单独调用删除每个选项。

参见

  • 使用数组存储用户设置的配方

在设置菜单中创建管理页面菜单项

在为插件配置选项定义默认值之后,下一步是创建一个用户可以查看和更改这些值的地方。通过使用 WordPress API,我们能够创建新的项目在管理菜单中,这将允许我们创建自定义插件配置页面。本配方展示了如何创建一个新菜单项,该菜单项将出现在管理菜单的设置子菜单下。

准备工作

您应该已经遵循了使用数组存储用户设置的菜谱,以便有选项数据可供管理。或者,您可以从下载的代码包中获取结果代码(第三章/ch2-page-header-output/ch2-page-header-output-v3.php)。在开始此菜谱之前,您应将文件ch2-page-header-output-v3.php重命名为ch2-page-header-output.php

如何做到这一点...

  1. 导航到您 WordPress 开发安装的根目录。

  2. 在文本编辑器中打开wp-config.php文件。

  3. WP_DEBUG变量设置为true

define('WP_DEBUG', true);
  1. 保存配置文件。

  2. 导航到您开发安装的 WordPress 插件目录中的ch2-page-header-output文件夹。

  3. 在文本编辑器中打开ch2-page-header-output.php文件。

  4. 添加以下代码行以注册一个在 WordPress 构建管理页面菜单时被调用的函数:

add_action( 'admin_menu', 'ch2pho_settings_menu' );
  1. 将以下代码段添加到为ch2pho_settings_menu函数提供实现的代码:
function ch2pho_settings_menu() {
    add_options_page( 'My Google Analytics Configuration',
                      'My Google Analytics', 'manage_options',
                      'ch2pho-my-google-analytics', 
                      'ch2pho_config_page' );
}
  1. 保存并关闭插件文件。

  2. 导航到您开发 WordPress 安装的管理页面。

  3. 如果您在遵循上一个菜谱后未激活第二章 - 页面标题输出插件,请激活它。

  4. 点击左侧导航菜单中的设置部分以展开它。您将在树中看到一个名为“我的谷歌分析”的新菜单项,它是由刚刚添加到插件中的代码创建的。

图片

  1. 点击“我的谷歌分析”菜单项。您将看到一个错误消息显示,因为 WordPress 找不到用于填充配置页面的函数。一旦您执行了名为使用 HTML 渲染管理页面内容的菜谱,此错误将消失。

  2. wp-config.php文件中,将WP_DEBUG变量恢复到其默认值false

define('WP_DEBUG', false);
  1. 保存并关闭配置文件。

如果您在菜谱开始时没有将WP_DEBUG变量设置为true,那么在执行步骤 13 后,WordPress 将只显示一个空白页面,而不是显示错误消息。

它是如何工作的...

此菜谱的第一行代码注册了一个在 WordPress 构建管理菜单时被调用的函数。当它执行时,我们创建的自定义函数调用add_options_page函数向设置菜单添加一个项目。此函数有几个参数,我们将按以下方式查看:

add_options_page( $page_title, $menu_title, $capability,
                  $menu_slug, $function );

前两个参数是文本字符串,将供网站管理员可见,第一个将出现在浏览器标题栏或标签页标题中,第二个是将在设置菜单下出现的子菜单项的文本。

第三个参数稍微复杂一些,指的是能够查看和访问此菜单项所需的用户能力。在 WordPress 安装中创建用户时,每个用户都被分配了五个默认用户角色之一(订阅者、管理员、编辑器、作者或贡献者)。这些角色中的每一个都映射到一组权限,这些权限决定了具有此角色的用户可以执行的操作。有关角色及其关联权限的完整列表,请参阅 WordPress Codex 页面上的相关主题(codex.wordpress.org/Roles_and_Capabilities)。在此示例中,我们使用了用户能力manage_options,它分配给具有站点管理权限的用户以及在网络上工作时的超级管理员。

第四个菜单项,menu_slug,是一个将被 WordPress 内部使用的文本字符串,用于标识菜单项。此字符串应该是唯一的,以避免与其他插件发生冲突。

menu_slug名称应全部小写,以确保更高级的功能,如 WordPress 元框,能够正常工作。

最后一个参数指定当点击子菜单项时调用以显示配置页面内容的函数名称。

设置菜单是只要求单个配置页面的插件的理想位置,正如您在安装其他插件时所见,而需要多个菜单部分的更复杂插件应使用下一道菜谱中所示的技术。

还有更多...

虽然新项目始终位于 WordPress 创建的默认设置菜单项(一般、写作、阅读等)之下,但插件开发者确实可以控制他们的插件在列表中的位置。

设置钩子优先级以确定菜单顺序

如前一章所述,当动作钩子首次引入时,add_action函数的第三个参数用于指示已注册的回调函数相对于为同一钩子(在这种情况下,admin_menu钩子)注册的其他函数的优先级。为了确保新创建的菜单项在菜单中尽可能高,可以设置注册函数的优先级为1

add_action( 'admin_menu', 'ch2pho_settings_menu', 1 );

应注意,其他插件也可以将它们的回调设置为这个优先级。在这种情况下,字母顺序优先级和激活顺序是确定永久链接之后哪个菜单项首先显示的其他因素。

参见

  • 使用数组存储用户设置的菜谱

创建多级管理菜单

当插件变得复杂时,它们的配置选项数量通常会增长,这使用户在选择插件在其网站上如何表现时具有很高的灵活性。虽然可以在单个长配置页面上显示所有插件选项,但创建一个包含多个部分的新顶级菜单项可以帮助将参数组织成逻辑分组,使用户能够更快地找到他们想要的内容。

此食谱展示了如何在管理菜单中创建一个带有相关子菜单项的新顶级菜单项。

如何做到...

  1. 导航到你的开发安装的 WordPress 插件目录。

  2. 创建一个名为 ch3-multi-level-menu 的新目录。

  3. 导航到该目录并创建一个名为 ch3-multi-level-menu.php 的新文本文件。

  4. 在代码编辑器中打开新文件,并在插件文件顶部添加一个适当的标题,将插件命名为 第三章 - 多级菜单

  5. 添加以下代码行以注册一个在 WordPress 准备显示网站管理菜单数据时将被调用的函数:

add_action( 'admin_menu', 'ch3mlm_admin_menu' );
  1. 将以下代码部分添加到为 ch3mlm_admin_menu 函数提供实现的代码段:
function ch3mlm_admin_menu() {
    // Create top-level menu item
    add_menu_page( 'My Complex Plugin Configuration Page',
                   'My Complex Plugin', 'manage_options',
                   'ch3mlm-main-menu', 'ch3mlm_my_complex_main',
                   plugins_url( 'myplugin.png', __FILE__ ) );

    // Create a sub-menu under the top-level menu
    add_submenu_page( 'ch3mlm-main-menu',
                      'My Complex Menu Sub-Config Page', 
                      'Sub-Config Page',
                      'manage_options', 'ch3mlm-sub-menu',
                      'ch3mlm_my_complex_submenu' );
}
  1. 保存并关闭插件文件。

  2. 从网站,如 IconArchive (www.iconarchive.com),找到一个 PNG 格式 24 x 24 像素的图标,将其调整大小为 20 x 20 像素,并将其保存为 myplugin.png 在插件目录中。

  3. 导航到你的网站管理区域的插件部分。

  4. 激活你的新插件。

  5. 你现在将在管理菜单中看到一个新菜单项。

图片

  1. 展开顶级新菜单项以查看子菜单项。

与前面的食谱类似,点击菜单项将显示错误,因为我们尚未实现为这些菜单项生成实际内容的函数。我们不会为这两个菜单项创建页面,但你可以使用与 使用 HTML 渲染管理页面内容 食谱中所示类似的技术来创建自己的 ch3mlm_my_complex_mainch3mlm_my_complex_submenu 函数。

它是如何工作的...

add_menu_page 函数与前面食谱中看到的 add_options_page 函数非常相似,其前五个参数是相同的:

add_menu_page( $page_title, $menu_title, $capability,
               $menu_slug, $function, $icon_url, $position );

最后两项是针对此功能的特定内容,第一项允许我们在菜单中显示自定义图标,位于我们的新顶级项旁边,第二项指定新菜单应在管理菜单中的位置。

虽然使用 add_menu_page 函数的 position 参数指定新菜单项的确切位置可能看起来很有趣,但这样做是危险的,因为如果两个插件创建具有相同 position 值的条目,则只会显示一个菜单项。如果没有指定 position 参数,新菜单项将出现在菜单结构的底部,这在大多数情况下应该是可以的。

一旦创建了第一个菜单项,就可以使用add_submenu_page函数来附加子菜单项。以下是其参数,它们与add_options_page函数几乎相同,除了第一个参数,它应该是要附加子菜单项的顶级菜单项的唯一字符串标识符:

add_submenu_page( $parent_slug, $page_title, $menu_title,
                  $capability, $menu_slug, $function );

虽然可以使用此技术为具有单个配置页面的插件创建顶级菜单项,但这些更简单的扩展应该在设置菜单下创建单个条目,如前一个配方中所示。

相关内容

  • 在设置菜单中创建管理页面菜单项的配方

添加指向外部页面的菜单项

虽然 WordPress 插件库在每个插件页面上提供许多有用的部分来托管重要信息,包括其描述、常见问题解答、截图和支持论坛,但许多开发者更喜欢将文档、常见问题解答甚至支持转移到他们自己的网站。这使他们能够为这些部分创建更多功能丰富的内容,并创建一个更定制的品牌体验,尤其是在提供免费和付费版本的插件的情况下。

本配方展示了如何添加将引导用户到外部网页的菜单项。

准备工作

您应该已经遵循了创建多级管理菜单的配方,以便在管理界面中设置多级菜单,并且生成的插件仍然处于活动状态。或者,您可以从下载的代码包中获取生成的代码(第三章/ch3-multi-level-menu/ch3-multi-level-menu.php)。

如何做到的...

  1. 导航到您开发安装的 WordPress 插件目录中的ch3-multi-level-menu文件夹。

  2. 在文本编辑器中打开ch3-multi-level-menu.php文件。

  3. add_submenu_page的两个调用之后,在ch3mlm_admin_menu函数的末尾添加以下代码行:

   global $submenu;
   $url = 'https://www.packtpub.com/books/info/packt/faq';
   $submenu['ch3mlm-main-menu'][] = array( 'FAQ', 'manage_options', $url );
  1. 保存并关闭插件文件。

  2. 刷新您开发 WordPress 安装的管理页面,以查看“我的复杂插件”菜单下的新子菜单项:

图片

它是如何工作的...

由于核心 WordPress 代码和所有活动插件都会调用菜单创建函数,系统会构建一个数组,包含将根据当前用户的访问级别显示的所有项目。本配方的代码访问包含此数组的 WordPress 全局变量,并在上一个配方中创建的ch3mlm-main-menu父菜单下简单地插入一个额外元素。添加的数据是一个包含三个元素的数组:菜单项的标签、查看项所需的权限以及将分配给新菜单链接的 URL。

尽管此代码在当前版本的 WordPress 中有效,并且对许多版本都有效,但如果 WordPress 更改构建菜单的方式,它可能在某个时候不再有效。话虽如此,如果对菜单构建方式进行了更改,很可能会引入新的 API 来重现此功能,因为它被广泛使用。

参见

  • 创建多级管理菜单的配方

隐藏用户不应访问的默认菜单项

许多用户称赞 WordPress 易于使用和简化的管理界面。尽管如此,几乎每个将其部署给新用户的用户都指示他们避免某些菜单项,因为他们不需要进入这些部分,如果他们修改这些区域的设置,可能会引入网站故障。

比通过培训预防更好的解决方案是使用几个简单的 API 函数来隐藏不需要的菜单项。本配方展示了如何使用这些函数来移除评论编辑器和永久链接设置菜单项。

如何操作...

  1. 导航到您的开发安装的 WordPress 插件目录。

  2. 创建一个名为ch3-hide-menu-item的新目录。

  3. 导航到该目录并创建一个名为ch3-hide-menu-item.php的新文本文件。

  4. 在代码编辑器中打开新文件,并在插件文件顶部添加一个适当的标题,将插件命名为第三章 - 隐藏菜单项

  5. 添加以下代码行以注册一个在 WordPress 准备显示网站导航菜单数据时将被调用的函数:

add_action( 'admin_menu', 'ch3hmi_hide_menu_item' );
  1. 将以下代码段添加到提供ch3hmi_hide_menu_item函数实现的实现中,隐藏评论菜单项:
function ch3hmi_hide_menu_item() {
    remove_menu_page( 'edit-comments.php' );
}
  1. ch3hmi_hide_menu_item函数添加额外的函数调用,以隐藏设置菜单下找到的永久链接子菜单项:
remove_submenu_page( 'options-general.php',
                     'options-permalink.php' );
  1. 保存并关闭插件文件。

  2. 导航到管理界面的插件部分。

  3. 激活您的新插件。

  4. 查看管理菜单以确认评论菜单不再可见:

图片

  1. 展开设置菜单以查看永久链接子菜单项也不可见。

它是如何工作的...

默认的 WordPress 管理菜单使用用于渲染每个部分的 PHP 代码文件的名称作为它们的唯一标识符。快速找到菜单项标识符的一种方法是在网页浏览器中将鼠标悬停在其上,并查看链接指向的地址。就评论菜单项而言,URL 是http://localhost/wp-admin/edit-comments.php;因此,在remove_menu_page调用中使用edit-comments.php

确定传递给remove_submenu_page函数的参数时使用了类似的技巧,确定设置部分有http://localhost/wp-admin/options-general.php的 URL,而永久链接部分有http://localhost/wp-admin/options-permalink.php的地址。

使用 HTML 渲染管理页面内容

一旦创建了一个自定义菜单项,WordPress 在访问它时会调用与其关联的函数。分配给该函数的主要目的是渲染一个包含所有用户可用选项的配置页面,并将捕获的数据发送回 WordPress 进行处理。

可以使用两种主要方法来渲染插件配置页面:直接 HTML 和设置 API。本食谱探讨了使用 HTML 创建配置面板的方法,而后续的食谱将展示如何使用设置 API 来准备页面输出。

准备工作

你应该已经遵循了在设置菜单中创建管理页面菜单项的食谱,并且生成的插件在你的开发站点上仍然处于活动状态。或者,你可以从下载的代码包中获取生成的代码(第三章/ch2-page-header-output/ch2-page-header-output-v4.php)。在开始本食谱之前,你应该将文件ch2-page-header-output-v4.php重命名为ch2-page-header-output.php

如何操作...

  1. 导航到你的开发安装的 WordPress 插件目录中的ch2-page-header-output文件夹。

  2. 在文本编辑器中打开ch2-page-header-output.php文件。

  3. 将以下代码行添加到实现插件选项页面渲染代码中:

function ch2pho_config_page() {
    // Retrieve plugin configuration options from database
    $options = ch2pho_get_options();
    ?>

    <div id="ch2pho-general" class="wrap">
    <h2>My Google Analytics</h2><br />

    <form method="post" action="admin-post.php"> 
    <input type="hidden" name="action"
           value="save_ch2pho_options" />

    <!-- Adding security through hidden referrer field -->
    <?php wp_nonce_field( 'ch2pho' ); ?>
    Account Name: <input type="text" name="ga_account_name"
     value="<?php echo esc_html( $options['ga_account_name'] );
     ?>"/><br />
    Track Outgoing Links: <input type="checkbox"
     name="track_outgoing_links"
     <?php checked( $options['track_outgoing_links'] ); ?>/>
    <br /><br />
    <input type="submit" value="Submit" class="button-primary"/>
    </form>
    </div>
<?php }
  1. 保存并关闭插件文件。

  2. 点击管理页面中的“设置”部分。

  3. 点击“我的 Google Analytics”菜单项以显示插件配置页面。

图片

它是如何工作的...

在配置页面实现函数内生成的任何输出都将被发送到浏览器,并包含在 WordPress 管理界面布局中。在本食谱的代码中,我们首先使用我们在此章中定义的ch2pho_get_options函数来检索插件的所有选项,方便地组织在一个数组中,我们可以将其存储在一个单独的变量中。

然后我们使用一个闭合的 PHP 括号来能够为函数体的其余部分编写直接的 HTML 代码,并将此内容直接发送到浏览器。HTML 代码负责创建一个标准表单,渲染一个文本字段以显示并接受新的 Google Analytics 账户号码值,以及一个复选框供用户指定是否应跟踪出站链接。最后,HTML 代码添加了一个提交按钮,允许用户提交对插件配置所做的任何更改。

仔细查看代码,它还包含了一些小的 PHP 代码片段,当显示选项页面时显示当前的配置值,并使用checked WordPress 实用函数在传递的参数为真时输出正确的 HTML 代码。

使用纯 HTML 渲染插件配置页面的最大优点是,它允许创建复杂的布局来向最终用户展示所有选项。这与我们在后面的菜谱中将要看到的使用设置 API 形成鲜明对比。HTML 对于许多网页设计师来说也比处理复杂函数更容易理解。

应该注意的是,由于我们没有实现处理提交数据并将其存储回options数据库表的代码,因此在此状态下提交的任何更改都不会被保存。这将在下一个菜谱中介绍。

还有更多...

一旦用户提交处理开始发挥作用,考虑安全性就变得非常重要,以确保避免最常见的应用程序安全风险(www.owasp.org/index.php/Top_10_2017-Top_10)。在这个菜谱中创建的表单也不例外。

wp_nonce_field

在这个菜谱中使用的wp_nonce_field函数是确保要提交的数据来自 WordPress 管理页面而不是外部来源的安全措施的一部分。通过添加此函数调用,将在插件配置表单中添加一个隐藏的文本字段,当接收到帖子数据时将检查其中的信息。

虽然这是可选的,但函数的第一个参数是一个唯一标识符,应始终设置以确保更好的安全性。如果没有设置,将使用默认值,这可能会促进安全漏洞。该函数还有许多其他可选参数,如下所示:

wp_nonce_field( [$action], [$name], [$referer], [$echo] );

其他三个参数用于指定 nonce 的名称,该名称需要在接收端进行匹配,一个布尔变量用于指示是否应设置 referer 字段以进行验证,以及另一个布尔参数用于确定是否应立即显示隐藏的表单字段或返回。

参见

  • 在设置菜单中创建一个管理页面菜单项的菜谱

  • 使用数组存储用户设置的菜谱

处理和存储插件配置数据

在配置页面就绪后,插件用户将能够修改配置选项并将它们提交以存储在 WordPress 数据库中。目前缺失的链接是创建一个数据处理函数,该函数将接收用户发布的数据并将其存储在网站的options表中。

这个菜谱描述了如何实现一个数据处理函数,以验证要存储的信息是合法的,并将信息存储在选项数组中。

准备工作

您应该已经遵循了“使用 HTML 渲染管理页面内容”的菜谱,并且生成的插件仍然在您的开发站点中激活。或者,您可以从下载的代码包中获取生成的代码(第三章/ch2-page-header-output/ch2-page-header-output-v5.php)。在开始此菜谱之前,您应该将文件 ch2-page-header-output-v5.php 重命名为 ch2-page-header-output.php

如何操作...

  1. 导航到您开发安装的 WordPress 插件目录中的 ch2-page-header-output 文件夹。

  2. 在文本编辑器中打开 ch2-page-header-output.php 文件。

  3. 添加以下代码行以注册一个在 WordPress 首次识别请求的页面是管理页面时被调用的函数:

add_action( 'admin_init', 'ch2pho_admin_init' );
  1. 将以下代码段添加到提供 ch2pho_admin_init 函数实现的步骤中:
function ch2pho_admin_init() {
    add_action( 'admin_post_save_ch2pho_options',
                'process_ch2pho_options' );
}
  1. 将以下代码段添加到提供 process_ch2pho_options 函数实现的步骤中,该函数在之前的步骤中已声明:
function process_ch2pho_options() { 
    // Check that user has proper security level
    if ( !current_user_can( 'manage_options' ) ) {
        wp_die( 'Not allowed' );
    }

    // Check if nonce field configuration form is present
    check_admin_referer( 'ch2pho' );

    // Retrieve original plugin options array
    $options = ch2pho_get_options();

    // Cycle through all text form fields and store their values
    // in the options array
    foreach ( array( 'ga_account_name' ) as $option_name ) {
        if ( isset( $_POST[$option_name] ) ) {
            $options[$option_name] =
                sanitize_text_field( $_POST[$option_name] );
        }
    }

    // Cycle through all check box form fields and set the options
    // array to true or false values based on presence of variables
    foreach ( array( 'track_outgoing_links' ) as $option_name ) {
        if ( isset( $_POST[$option_name] ) ) {
            $options[$option_name] = true;
        } else {
            $options[$option_name] = false;
        }
    }

    // Store updated options array to database
    update_option( 'ch2pho_options', $options );

    // Redirect the page to the configuration form
    wp_redirect( add_query_arg( 'page', 
                                'ch2pho-my-google-analytics',
                            admin_url( 'options-general.php' ) ) );
    exit;
}
  1. 保存并关闭插件文件。

  2. 点击管理菜单中的设置部分。

  3. 点击“我的 Google Analytics”菜单项以显示配置页面。

  4. 更改其中一个字段的值并点击提交按钮。

  5. 当页面刷新时,您将看到显示的值反映了提交的值。

它是如何工作的...

这个菜谱是第一个介绍具有变量名称的动作钩子的菜谱。在调用 add_action 时,不是编写特定的动作钩子名称,而是以单词 admin_post_ 开头,后跟它期望与隐藏表单字段匹配的动作名称。在这种情况下,动作名称是 save_ch2pho_options。回到之前的菜谱,您可以看到这段文本与放置在名为 action 的隐藏表单字段中的文本相同:

<input type="hidden" name="action" value="save_ch2pho_options" />

当配置页面表单提交时,它将所有数据发送到 admin-post.php 脚本,该脚本检查 action 字段,然后将它接收到的数据发送到关联的函数(如果存在)。

一旦执行了处理函数,对 current_user_cancheck_admin_referer 的调用是安全措施,其中我们检查当前登录的用户是否具有管理权限,以及表单的一部分是否包含 nonce 字段。这些权限检查中的错误将导致特定的错误消息,告知用户他没有执行此操作的权限,而 nonce 检查将显示一个模糊的错误消息以迷惑潜在的攻击者:

图片

函数的其余部分专注于使用ch2pho_get_options函数检索当前插件选项集,处理提交的字段,并将更新的值存储回站点数据库。虽然使用foreach循环来存储两个简单的数据字段可能看起来有些过度,但这种方法可以轻松扩展以支持大量的配置字段。

最后一步是调用wp_redirect函数,在所有数据存储完毕后将浏览器重定向回插件选项页面。在调用wp_redirect之后始终调用exit PHP 函数非常重要,如本配方中所示。

另请参阅

  • 使用 HTML 渲染管理页面内容的配方

在保存选项时显示确认消息

任何用户界面的一个重要可用性方面是在用户成功完成任务后显示确认消息。正如你可能在前一个配方中注意到的,WordPress 在将配置数据保存到选项表后默认不提供任何用户反馈。

本配方解释了如何在用户更新插件配置选项后在配置页面上显示确认消息。

准备工作

你应该已经遵循了处理和存储插件配置数据配方,并且生成的插件仍然在你的开发站点中处于活动状态。或者,你可以从下载的代码包中获取生成的代码(Chapter 3/ch2-page-header-output/ch2-page-header-output-v6.php)。在开始本配方之前,你应该将文件ch2-page-header-output-v6.php重命名为ch2-page-header-output.php

如何做...

  1. 导航到开发安装的 WordPress 插件目录中的ch2-page-header-output文件夹。

  2. 在文本编辑器中打开ch2-page-header-output.php文件。

  3. 修改process_ch2pho_options函数末尾对wp_redirect的调用,如下所示,加粗部分表示修改:

wp_redirect( add_query_arg(
                 array( 'page' => 'ch2pho-my-google-analytics',
 'message' => '1' ),
 admin_url( 'options-general.php' ) ) );
  1. ch2pho_config_page函数中配置页面标题之后添加以下代码(加粗):
<h2>My Google Analytics</h2><br />

<?php if ( isset( $_GET['message'] ) &&
           $_GET['message'] == '1' ) { ?>
 <div id='message' class='updated fade'>
    <p><strong>Settings Saved</strong></p></div> <?php } ?>
  1. 保存并关闭插件文件。

  2. 点击管理菜单中的设置部分。

  3. 点击“我的谷歌分析”菜单项。

  4. 更改其中一个字段的值并点击提交按钮,以查看新创建的消息,指示已保存设置。

图片

工作原理...

当执行重定向调用时,用户提交的字段和 PHP 变量不会传递到目标页面。因此,我们需要使用另一种方法,即查询参数,来确定应该显示确认消息。

配方的第一部分稍微修改了现有的wp_redirect调用,添加了一个名为message的新查询变量,并将其设置为值1

一旦接收到这个变量,负责渲染选项页面的代码可以显示一条消息,遵循标准的 WordPress 样式。

同样的机制可以用来根据选项存储的结果显示不同的消息。例如,如果某些字段需要以某种方式格式化接收数据,process_ch2pho_options函数可以根据数据处理操作的成功或失败设置不同的消息值。

参见

  • 处理和存储插件配置数据的配方

添加自定义帮助页面

尽管字段标签可以尽可能详细,但一个好的插件总是需要一套文档来帮助用户快速了解如何激活插件并执行正确的步骤以获得预期的结果。虽然ReadMe文件通常是开发者首先想到要生产的,但用户几乎从不阅读外部文件或官方 WordPress 插件页面上的说明,他们只是安装插件并试图自己弄清楚。

为了解决这个问题,WordPress 引入了在插件管理页面中创建详细的多节帮助页面的能力,使用户能够快速找到问题的答案。这个配方展示了如何注册适当的回调函数,将帮助部分添加到你的插件配置页面,包含多个信息标签页。

准备工作

你应该已经遵循了保存选项时显示确认消息的配方,并且生成的插件仍然在你的开发站点上处于活动状态。或者,你可以从下载的代码包中获取生成的代码(Chapter 3/ch2-page-header-output/ch2-page-header-output-v7.php)。在开始此配方之前,你应该将文件ch2-page-header-output-v7.php重命名为ch2-page-header-output.php

如何做到...

  1. 导航到你的开发安装的 WordPress 插件目录中的ch2-page-header-output文件夹。

  2. 在文本编辑器中打开ch2-page-header-output.php文件。

  3. 在现有代码中找到ch2pho_settings_menu函数。

  4. 修改代码以将add_options_page函数调用的返回值存储到变量中:

$options_page = 
    add_options_page( 'My Google Analytics Configuration', 
                      'My Google Analytics',
                      'manage_options',
                      'ch2pho-my-google-analytics',
                      'ch2pho_config_page' );
  1. 将以下代码块添加到ch2pho_settings_menu函数中,以注册一个在插件选项页加载时将被调用的操作:
if ( !empty( $options_page ) ) {
    add_action( 'load-' . $options_page, 'ch2pho_help_tabs' );
}
  1. 在插件文件末尾添加以下代码以实现新声明的ch2pho_help_tabs函数:
function ch2pho_help_tabs() {
    $screen = get_current_screen();
    $screen->add_help_tab( array(
            'id'       => 'ch2pho-plugin-help-instructions',
            'title'    => 'Instructions',
            'callback' => 'ch2pho_plugin_help_instructions',
        ) );

    $screen->add_help_tab( array(
             'id'       => 'ch2pho-plugin-help-faq',
             'title'    => 'FAQ',
             'callback' => 'ch2pho_plugin_help_faq',
        ) );

    $screen->set_help_sidebar( '<p>This is the sidebar
        content</p>' );
}
  1. 将以下代码段添加到提供ch2pho_plugin_help_instructions函数实现的代码中:
function ch2pho_plugin_help_instructions() { ?>
    <p>These are instructions explaining how to use this 
       plugin.</p>
<?php }
  1. 将以下代码段添加到提供ch2pho_plugin_help_faq函数实现的代码中:
function ch2pho_plugin_help_faq() { ?>
    <p>These are the most frequently asked questions on the use of
       this plugin.</p>
<?php }
  1. 保存并关闭插件文件。

  2. 点击管理菜单中的“设置”部分。

  3. 点击“我的 Google Analytics”菜单以显示插件配置页面。现在你将在页面的右上角看到一个帮助标签。

  4. 点击帮助标签以查看已添加到插件的所有帮助内容。

图片

它是如何工作的...

如在 处理和存储插件配置数据 食谱中首次讨论的那样,一些 WordPress 动作钩子的名称包含一个变量元素,允许插件开发者获取在渲染特定页面或提交特定表单数据时执行的代码。在这个例子中,load-<pagename> 钩子用于注册一个函数,当用户访问特定的管理页面时,该函数将被执行。

一旦回调发生,函数的代码会检索 WordPress 屏幕对象的引用,该对象包含有关当前显示的屏幕的数据,以及一些用于操作和向页面添加内容的实用函数。食谱中的代码随后继续注册函数,使用 add_help_tab 函数渲染帮助标签中两个部分的内容。

add_help_tab 函数与我们之前看到的函数略有不同,它期望一个包含单个选项数组的参数。这些选项指示菜单部分的唯一标识符、每个标签上显示的标题以及将渲染标签内容的函数的名称。还可以用名为 content 的参数替换回调参数,它将直接包含要在帮助标签中显示的 HTML 代码。有了这些信息,WordPress 能够在渲染选项页面界面时集成提供的 HTML 代码,包括所有必要的包装代码,以便帮助标签可以打开和关闭,并允许用户在不同部分之间切换。

在这个食谱中使用的另一个功能 set_help_sidebar 比使用 add_help_tab 的功能还要简单,它只需要一个参数来指示要在帮助部分右侧显示的 HTML 内容。

参见

  • 使用 HTML 渲染管理页面内容 的食谱

使用设置 API 渲染管理页面内容

除了通过 HTML 代码创建管理页面之外,WordPress 还提供了一套称为设置 API 的函数,可用于自动化创建复杂的配置页面。虽然将这种渲染技术应用于仅有一小部分选项的插件可能有些过度,但如果您要处理数十或数百个配置字段,这无疑非常有用,因为它简化了为每个单独的项目编写 HTML 代码的任务,只需调用每个项目的单个函数即可。它还提供了一些关于插件配置数据处理和存储的自动化功能。

这个食谱解释了如何使用设置 API 指定配置页面的内容,以及如何为配置页面中最常用的表单字段类型提供渲染函数。它使用了本章中其他食谱相同的配置选项集,以展示两种技术的比较。

如何做到...

  1. 导航到您的开发安装的 WordPress 插件目录。

  2. 创建一个名为ch3-settings-api的新目录。

  3. 导航到该目录并创建一个名为ch3-settings-api.php的新文本文件。

  4. 在代码编辑器中打开新文件,并在插件文件顶部添加一个适当的标题,将插件命名为“第三章 - 设置 API”。

  5. 添加以下代码行以注册一个函数,该函数将在 WordPress 激活插件时被调用:

register_activation_hook( __FILE__, 
                          'ch3sapi_set_default_options' );
  1. 将以下代码段添加到提供ch3sapi_set_default_options函数实现的代码中,以设置默认插件选项:
function ch3sapi_set_default_options() {
    ch3sapi_get_options();
}
  1. 添加以下代码以提供ch3sapi_get_options函数的实现:
function ch3sapi_get_options() {
    $options = get_option( 'ch3sapi_options', array() );

    $new_options['ga_account_name'] = 'UA-0000000-0';
    $new_options['track_outgoing_links'] = false;

    $merged_options = wp_parse_args( $options, $new_options );

    $compare_options = array_diff_key( $new_options, $options );
    if ( empty( $options ) || !empty( $compare_options ) ) {
        update_option( 'ch3sapi_options', $merged_options );
    }
    return $merged_options;
}
  1. 添加以下注册函数以将回调与admin_init动作钩子相关联:
add_action( 'admin_init', 'ch3sapi_admin_init' );
  1. ch3sapi_admin_init函数添加实现,创建插件的设置组并定义其内容:
function ch3sapi_admin_init() {
    // Register a setting group with a validation function
    // so that post data handling is done automatically for us 
    register_setting( 'ch3sapi_settings',
        'ch3sapi_options', 'ch3sapi_validate_options' );

    // Add a new settings section within the group
    add_settings_section( 'ch3sapi_main_section', 'Main Settings',
        'ch3sapi_main_setting_section_callback',
        'ch3sapi_settings_section' );

    // Add each field with its name and function to use for
    // our new settings, put them in our new section 
    add_settings_field( 'ga_account_name', 'Account Name',
        'ch3sapi_display_text_field', 'ch3sapi_settings_section',
        'ch3sapi_main_section',
        array( 'name' => 'ga_account_name' ) );

    add_settings_field( 'track_outgoing_links',
        'Track Outgoing Links', 'ch3sapi_display_check_box',
        'ch3sapi_settings_section', 'ch3sapi_main_section',
        array( 'name' => 'track_outgoing_links' ) );
}
  1. 声明ch3sapi_validate_options函数的主体,该函数在上一节注册设置时声明:
function ch3sapi_validate_options( $input ) {
    foreach ( array( 'ga_account_name' ) as $option_name ) { 
        if ( isset( $input[$option_name] ) ) { 
            $input[$option_name] = 
                sanitize_text_field( $input[$option_name] ); 
        } 
    } 

    foreach ( array( 'track_outgoing_links' ) as $option_name ) { 
        if ( isset( $input[$option_name] ) ) { 
            $input[$option_name] = true; 
        } else { 
            $input[$option_name] = false; 
        } 
    }
    return $input;  
}
  1. 声明ch3sapi_main_setting_section_callback函数的主体,该函数在创建设置部分时声明:
function ch3sapi_main_setting_section_callback() { ?>
    <p>This is the main configuration section.</p>
<?php }
  1. 为在设置部分添加文本字段时声明的ch3sapi_display_text_field函数提供实现:
function ch3sapi_display_text_field( $data = array() ) {
    extract( $data );
    $options = ch3sapi_get_options();
    ?>
    <input type="text" name="ch3sapi_options[<?php echo $name; ?>]"
           value="<?php echo esc_html( $options[$name] ); ?>"/>
    <br />
<?php }
  1. 声明并定义ch3sapi_display_check_box函数,当在设置部分添加复选框时声明:
function ch3sapi_display_check_box( $data = array() ) {
    extract ( $data );
    $options = ch3sapi_get_options();
    ?>
    <input type="checkbox" 
           name="ch3sapi_options[<?php echo $name;  ?>]"
           <?php checked( $options[$name] ); ?>/>
<?php }
  1. 添加以下代码行以注册一个函数,该函数将在 WordPress 准备数据以显示站点管理菜单时被调用:
add_action( 'admin_menu', 'ch3sapi_settings_menu' );
  1. ch3sapi_settings_menu函数的实现提供代码:
function ch3sapi_settings_menu() {
    add_options_page( 'My Google Analytics Configuration',
        'My Google Analytics - Settings API', 'manage_options',
        'ch3sapi-my-google-analytics', 'ch3sapi_config_page' );
}
  1. 为在声明新选项页面时定义的ch3sapi_config_page函数添加定义:
function ch3sapi_config_page() { ?>
    <div id="ch3sapi-general" class="wrap">
    <h2>My Google Analytics - Settings API</h2>

    <form name="ch3sapi_options_form_settings_api" method="post"
          action="options.php">

    <?php settings_fields( 'ch3sapi_settings' ); ?>
    <?php do_settings_sections( 'ch3sapi_settings_section' ); ?>

    <input type="submit" value="Submit" class="button-primary" />
    </form></div>
<?php }
  1. 保存并关闭插件文件。

  2. 导航到管理区域的插件菜单。

  3. 激活您的新插件。

  4. 导航到设置菜单并点击“我的谷歌分析 - 设置 API”菜单项,以查看此插件的配置页面。

图片

  1. 对选项进行修改并提交,以查看 WordPress 是否能够自动处理这些选项,而无需编写保存选项的显式代码。

它是如何工作的...

设置 API 是一个复杂的回调系列,允许插件开发者简化管理页面的创建,并自动存储用户选项。

这个自包含的插件配方从创建一组新的默认选项开始,以避免意外删除先前配方中的选项。

代码继续注册一个函数,当使用admin_init动作钩子准备显示管理页面时将被调用。在调用时,回调函数负责注册一个新的设置组、属于此组的设置节段以及将在节段内显示所需选项的两个字段。如本代码所示,注册了额外的函数以验证用户提交的数据、在节段开头显示自定义文本以及显示捕获和显示用户输入所需的不同类型的字段。

仔细查看刚刚使用的每个函数,第一个函数有三个参数,如下所示:

register_setting( $option_group, $option_name, $sanitize_callback );

在这些参数中,第一个选项是设置组的唯一标识符,第二个是用于在站点数据库中存储配置数据的选项数组的名称,而第三个是用于接收用户输入进行验证的回调函数的名称。

接下来是本例中使用的第二个函数add_settings_section,它所需的四个参数分别表示节段的唯一标识符、在渲染节段时显示的标题字符串、用于为节段显示描述的回调函数,以及最后用于在插件代码中渲染所有类似函数的页面标识符:

add_settings_section( $id, $title, $callback, $page );

在本食谱中使用的 Settings API 的第三个函数add_settings_field被多次调用,以定义构成每个节段的字段:

add_settings_field( $id, $title, $callback, $page, $section, [$args] );

与其他函数类似,第一个参数是字段的唯一标识符,第二个参数是将在字段旁边显示的标签,第三个参数是执行以输出显示字段的必要 HTML 代码的回调函数。接下来的三个参数表示字段所属的页面、包含字段的节段,以及可选的数组,其中包含要发送到回调函数的额外数据。如本食谱的其余部分所示,我们利用这个可选的额外数据参数将数据发送到字段处理函数,使它们更加通用。

当访问配置页面时,使用常规 HTML 代码创建顶级表单,将操作设置为options.php。此脚本负责自动化处理用户数据。表单的其余部分相当简单,因为它是由settings_fieldsdo_settings_sections函数生成的。

当它们被调用时,将渲染之前创建的设置组,然后调用设计用于绘制它包含的所有节段以及这些节段中注册的所有字段的函数。

虽然设置 API 提供了对表单字段布局的完全控制,但其使用决定了配置页面的总体布局,创建了一个包含每个字段标签的第一列和由插件的回调函数生成的代码的第二列的两列表格。当调用每种类型字段的函数时,它们会接收到与每个字段相关联的数组数据,并使用它来检索当前字段的值,并指定每个字段在用户输入后要存储的名称。

最后一个拼图是当设置组首次创建时注册的验证函数。此函数的目的是允许插件开发者在使用表单提交用户数据时执行数据类型或内容验证,类似于我们在处理和存储插件配置数据配方中验证用户提交数据的方式。

更多...

虽然这个配方展示了如何为两种数据字段创建渲染函数,但你可能需要为你的插件提供其他类型的选项。以下是一些代码示例,展示了如何处理在插件选项中使用的最典型数据类型。

渲染下拉列表设置字段

在添加选择列表本身之前,我们首先会在ch3sapi_get_options函数中添加一行来初始化select_list选项,如果未在从数据库检索的选项中找到:$new_options['select_list'] = 'First';

渲染下拉列表的下一步是提供所有可能的选项列表,包括选项名称,在可选的字段数据数组中。以下是一个调用add_settings_field函数的示例,其中包含此类列表:

add_settings_field( 'select_list', 'Select List',
    'ch3sapi_select_list', 'ch3sapi_settings_section',
    'ch3sapi_main_section',
    array( 'name' => 'select_list',
           'choices' => array( 'First', 'Second', 'Third' ) ) );

使用这些信息,我们可以为ch3sapi_select_list函数提供一个实现,该函数将能够使用choices数组来渲染 HTML 选择元素:

function ch3sapi_select_list( $data = array() ) {
    extract ( $data );
    $options = ch3sapi_get_options();
    ?>
    <select name="ch3sapi_options[<?php echo $name; ?>]">
        <?php foreach( $choices as $item ) { ?>
        <option value="<?php echo $item; ?>"
        <?php selected( $options[$name] == $item ); ?>>
        <?php echo $item; ?></option>;
    <?php } ?>
    </select>
<?php }

渲染文本区域设置字段

配置页面中常用的另一种字段类型是多行文本区域。同样,如果未找到此新选项的默认值,我们需要更新ch3sapi_get_options函数。完成此操作后,add_settings_field函数与配方中显示的文本和复选框示例相同,而字段渲染代码如下:

function ch3sapi_display_text_area( $data = array() ) {
    extract ( $data );
    $options = ch3sapi_get_options();
    ?>
    <textarea type="text" name="ch3sapi_options[<?php echo $name; ?>]"
              rows="5" cols="30">
        <?php echo esc_html ( $options[$name] ); ?></textarea>
<?php }

参见

  • 使用 HTML 渲染管理页面内容的配方

  • 处理和存储插件配置数据的配方

从操作和过滤器钩子访问用户设置

在为我们的插件配置创建默认值集并创建一个界面以允许用户修改和更新这些值之后,我们现在可以开始使用这些选项在页面使用我们的附加插件功能渲染时使用。回到在第二章中创建的 Google Analytics 示例,插件框架基础,这个做法展示了如何使用熟悉的函数访问插件选项数据,从而使现有代码更加灵活。

准备工作

您应该已经遵循了添加自定义帮助页面的做法,并且生成的插件应在您的开发站点中处于活动状态。或者,您可以从下载的代码包中获取生成的代码(Chapter 3/ch2-page-header-output/ch2-page-header-output-v8.php)。在开始此做法之前,您应将文件ch2-page-header-output-v8.php重命名为ch2-page-header-output.php

如何做到这一点...

  1. 导航到您开发安装的 WordPress 插件目录中的ch2-page-header-output文件夹。

  2. 在文本编辑器中打开ch2-page-header-output.php文件。

  3. 修改ch2pho_page_header_output函数的实现以检索插件选项数组并使用存储的账户号码将其嵌入页面页眉代码中。新的部分以粗体显示:

function ch2pho_page_header_output() {
    $options = ch2pho_get_options();
    ?>
    <script>
        (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;
        i[r]=i[r]||function(){
        (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();
        a=s.createElement(o),
        m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;
        m.parentNode.insertBefore(a,m)})(window,document,'script',
        'https://www.google-analytics.com/analytics.js','ga');

        ga( 'create', '<?php echo $options['ga_account_name']; ?>',
            'auto' );
        ga('send', 'pageview');
    </script>
<?php }
  1. 在注册动作钩子以过滤所有帖子内容和页面内容之前,添加代码以检查是否应该进行出站代码跟踪,其中所做的更改以粗体显示:
$options = ch2pho_get_options();

if ( true == $options['track_outgoing_links'] ) {
    add_filter( 'the_content','ch2lfa_link_filter_analytics' );
}
  1. 使用相同的检查来确定是否应该添加页面页脚代码以提供出站链接跟踪所需的 JavaScript,其中所做的更改以粗体显示:
if ( true == $options['track_outgoing_links'] ) { 
    add_action( 'wp_footer', 'ch2lfa_footer_analytics_code' ); 
}
  1. 保存并关闭插件文件。

  2. 访问网站并查看页面源代码,以查看之前的UA-0000000-0已被插件配置页面中保存的最后一个值所替换。您还可以通过更改跟踪出站链接选项来设置是否显示链接跟踪代码。

它是如何工作的...

如本章前面所述,在创建管理页面时,我们的自定义ch2pho_get_options函数可以查询网站的数据库并返回它包含的插件配置数据。这些数据可以是以单个变量或信息数组的形式。在这种情况下,根据本章前面找到的使用数组存储用户设置的做法,使用了一个数组并访问它,在调用页眉和页脚动作钩子以及页面内容正在被过滤时注入值。

参见

  • 使用数组存储用户设置的做法

使用元框格式化管理页面

随着插件的管理页面变得越来越长和复杂,将其内容分成多个部分变得很重要。虽然标准的 HTML 标题或fieldset标签可以用于此任务,但它们缺乏元框的实用性和美观的视觉表现。元框是出现在大多数默认 WordPress 内容编辑器和主管理仪表板页面上的容器。

除了视觉组织内容外,元框非常强大,因为它们允许网站管理员折叠他们不使用的配置部分,根据他们的需求重新排序部分,甚至隐藏他们不使用的元素。

本食谱解释了如何将本章早期创建的基于 HTML 的配置页面转换为使用内置元框系统。

准备工作

你应该已经遵循了“从动作和过滤器钩子访问用户设置”的食谱。或者,你可以从下载的代码包中获取结果代码(第三章/ch2-page-header-output/ch2-page-header-output-v9.php)。在开始食谱之前,你应该将文件ch2-page-header-output-v9.php重命名为ch2-page-header-output.php

如何操作...

  1. 浏览到您网站管理区域的插件部分,并停用第二章 - 页面头部输出插件。

  2. 导航到您开发安装的 WordPress 插件目录中的ch2-page-header-output文件夹。

  3. 将文件ch2-page-header-output.php复制到ch2-page-header-output-metaboxes.php

  4. 在文本编辑器中打开ch2-page-header-output-metaboxes.php文件。

  5. 将标题中的插件名称从第二章 - 页面头部输出更改为第二章 - 页面头部输出元框

  6. 在顶部插件注释下方,添加一行代码来声明一个全局变量以保存选项页的标识符:

global $options_page;
  1. 在现有代码中找到ch2pho_settings_menu函数。

  2. 在函数顶部添加一行以指向全局选项页面变量:

global $options_page;
  1. 在插件代码中找到ch2pho_help_tabs函数。

  2. 在函数体末尾添加以下代码块以创建要在屏幕上绘制的元框,并指定填充这些框的函数:

global $options_page;

add_meta_box( 'ch2pho_general_meta_box', 'General Settings',
              'ch2pho_plugin_meta_box', $options_page,   
              'normal', 'core' );

add_meta_box( 'ch2pho_second_meta_box', 'Second Settings Section',
              'ch2pho_second_meta_box', $options_page, 
              'normal', 'core' );
  1. 在插件代码文件末尾添加一行代码,以注册一个当管理页面样式被放入队列时调用的函数:
add_action( 'admin_enqueue_scripts', 'ch2pho_load_admin_scripts' );
  1. 插入以下代码段以提供ch2pho_load_admin_scripts函数的实现:
function ch2pho_load_admin_scripts() {
    global $current_screen;
    global $options_page;

    if ( $current_screen->id == $options_page ) {
        wp_enqueue_script( 'common' );
        wp_enqueue_script( 'wp-lists' );
        wp_enqueue_script( 'postbox' );
    }
}
  1. 创建一个新的函数来实现之前声明过的ch2pho_plugin_meta_box函数。注意,函数体是直接复制粘贴之前用于渲染“账户名称”和“跟踪外链”字段的代码:
function ch2pho_plugin_meta_box( $options ) { ?>
    Account Name: <input type="text" name="ga_account_name"
        value="<?php echo esc_html( $options['ga_account_name'] );
        ?>"/><br />

    Track Outgoing Links <input type="checkbox"
        name="track_outgoing_links" 
        <?php checked( $options['track_outgoing_links'] ); ?>/>
        <br /> 
<?php }
  1. 添加以下代码以提供 ch2pho_second_meta_box 函数的实现来显示第二个元框。这个第二个框将没有任何实际内容。它仅用于说明一些元框功能:
function ch2pho_second_meta_box( $options ) { ?>
    <p>This is the content of the second metabox.</p>
<?php }
  1. 在你的代码中找到 ch2pho_config_page 函数的代码,并按以下代码进行修改,其中所有新的代码段都加粗。删除渲染 ga_account_nametrack_outgoing_links 字段的原始代码:
function ch2pho_config_page() {
    // Retrieve plugin configuration options from database
    $options = ch2pho_get_options();
    global $options_page;
    ?>

    <div id="ch2pho-general" class="wrap">
    <h2>My Google Analytics</h2><br />

    <?php if ( isset( $_GET['message'] ) && 
               $_GET['message'] == '1' ) { ?>
        <div id='message' class='updated fade'>
        <p><strong>Settings Saved</strong></p>
        </div>
    <?php } ?>

    <form action="admin-post.php" method="post">
    <input type="hidden" name="action"
           value="save_ch2pho_options" />

    <!-- Adding security through hidden referrer field -->
    <?php wp_nonce_field( 'ch2pho' ); ?>

    <!-- Security fields for meta box save processing -->
 <?php wp_nonce_field( 'closedpostboxes',                          'closedpostboxesnonce', false ); ?>
 <?php wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false ); ?>

    <div id="poststuff" class="metabox-holder">
 <div id="post-body">
 <div id="post-body-content">
 <?php do_meta_boxes( $options_page, 'normal', 
                                                $options ); ?>
            <input type="submit" value="Submit"
                   class="button-primary"/>
            </div>
 </div>
 <br class="clear"/>
 </div>
    </form>
    </div>

    <script type="text/javascript">
 //<![CDATA[
 jQuery( document ).ready( function( $ ) {
 // close postboxes that should be closed
 $( '.if-js-closed' ) .removeClass( 'if-js-closed' ).
 addClass( 'closed' );

 // postboxes setup
 postboxes.add_postbox_toggles(
            '<?php echo $options_page; ?>' );
 });

 //]]>
 </script>
<?php }
  1. 保存并关闭插件文件。

  2. 激活你的新插件。

  3. 点击左侧导航菜单中的设置部分以展开它。

  4. 在树中点击“我的 Google Analytics”,以显示重新设计的管理页面。

图片

  1. 拖放其中一个元框以重新排序它们。

  2. 点击其中一个元框标题以折叠该部分。

  3. 点击右上角的屏幕选项菜单以打开一个菜单来控制所有元框的可见性。

  4. 将鼠标移至管理菜单的另一部分,然后返回到“我的 Google Analytics”部分,你会看到对配置页面布局所做的所有更改都已保留。

它是如何工作的...

元框功能设置是在 load-<pagename> 回调函数中完成的,通过多次调用 add_meta_box 函数,根据屏幕上要显示的框的数量。

函数接受多个参数,如下所示:

add_meta_box( $id, $title, $callback, [$page], [$context], [$priority],
              [$callback_args] );

在此函数中查看参数,第一个是元框的唯一标识符,第二个是作为框本身标题显示的字符串,也是将在屏幕选项配置标签中显示的名称。第三个参数是要调用的函数名称,用于渲染元框的内容。第四个参数标识将渲染元框的页面。在这种情况下,我们使用全局变量 $options_page 的值作为此参数,以确保它将分配正确的页面标识符。全局 PHP 变量是强大的工具,可以帮助我们在插件中的函数之间共享数据。通过在变量名称前使用关键字 global,网站的 PHP 解释器将知道它必须访问公共内存空间以存储和访问信息。

第五个参数是一个任意名称,表示应该显示框的章节名称。当向 WordPress 发送请求以渲染属于特定章节的所有元框时,将使用此名称。为此功能正确工作,唯一的要求是在调用 do_meta_boxes 函数时使用相同的名称。

第六个参数表示注册的元框在其所属部分内的优先级,相对于其他元框。如果所有框具有相同的优先级,则调用add_meta_box函数的顺序将决定它们的原始绘制顺序。当然,正如在这个食谱中所看到的,这个顺序可以通过用户的简单拖放操作来覆盖。最后一个参数是可选的,可以用来向将渲染元框内容的函数发送信息。

虽然实际上可以从其他动作钩子回调中调用add_meta_box,但只有在一个load-<pagename>回调期间注册的元框会显示在屏幕选项列表中,以便用户控制它们的可见性。您可能故意在动作钩子之外创建元框,以确保重要的框总是显示出来,并为所有用户提供标准化的用户体验。

除了调用add_meta_box之外,我们还需要在页面加载函数中多次调用wp_enqueue_script,以请求在渲染我们的配置页面时加载三个 JavaScript 脚本。这些脚本提供了在食谱末尾展示的拖放、最小化和隐藏功能,只需要从我们的代码中通过 JavaScript 函数执行少量初始化调用。

一旦创建了元框,大部分工作都是在选项页面渲染函数中完成的。正如我们在修改后的代码中所看到的,首先要做的是创建新的 nonce 字段。这些唯一的数字将作为页面中的隐藏数据生成,并将用于在配置页面中保存布局更改的认证。接下来,我们创建了一系列具有特定id名称的div部分,其中包含对do_meta_boxes函数的嵌套调用。这些 div 标签用于确保元框使用 WordPress 管理页面样式表进行样式化。

一旦调用,do_meta_boxes函数将负责绘制为给定页面(在第一个参数中指定)和给定部分(第二个参数中指定)创建的所有元框。它还将任何在第三个函数参数中指定的数据传递给与每个框关联的函数。

页面渲染函数的其余更改是一段 JavaScript 代码,负责关闭用户在之前访问页面时关闭的任何元框部分。它还分配 jQuery 回调到元框上,以便任何用户与它们的交互都会通过向 Web 服务器发送 AJAX 请求保存到网站数据库中。

最后但同样重要的是,元框渲染函数负责渲染每个元框内的内容。它们可以通过输出纯 HTML 来实现这一点。通过将这些函数的完整选项数组传递给这些函数,它们包含的代码可以与之前完全相同,以渲染各种选项字段。

参见

  • 使用 HTML 渲染管理页面内容配方

将管理代码从主插件文件中分离出来以优化网站性能

如第二章中所述,插件框架基础,WordPress 插件的主代码文件的内容在网站上渲染任何页面时都会被评估,无论是面向访客的页面还是后端管理页面。这意味着大量的 PHP 代码可能会在每次迭代中解析,即使当常规访客浏览网站时,其中一些代码也永远不会激活,这会浪费网站服务器的处理能力。

这方面的一个典型例子就是我们在本章中构建的所有代码示例。虽然这些代码对于网站管理员非常有用,但在常规页面显示时,没有理由让 Web 服务器解析和验证这些代码。因此,最好将这些代码隔离到单独的文件中,只有当有人访问网站仪表板时才会加载和解析。以下配方展示了如何将不太经常需要的代码隔离到单独的文件中,并且只在用户访问网站管理部分时加载它。

准备工作

你应该已经遵循了从默认菜单中隐藏用户不应访问的项目配方,以便为本配方提供一个起点,并且生成的插件仍然在你的开发网站上处于活动状态。或者,你可以从下载的代码包中获取生成的代码(Chapter 3/ch3-hide-menu-item/ch3-hide-menu-item.php)。

如何操作...

  1. 导航到你的开发安装的 WordPress 插件目录中的ch3-hide-menu-item文件夹。

  2. 在文本编辑器中打开ch3-hide-menu-item.php文件。

  3. 在同一目录下创建一个新的 PHP 代码文件,命名为ch3-hide-menu-item-admin-functions.php,并在文本编辑器中打开它。

  4. 将对add_action函数的调用和ch3hmi_hide_menu_item函数的定义移动到新文件中,在标准 PHP 开放标签之前,并检查常量的定义:

<?php

if ( !defined( 'ch3hmi' ) ) {
    exit;
}
  1. 在主插件代码文件(ch3-hide-menu-item.php)中,添加代码以定义一个常量并检查当前正在渲染的页面是否为管理页面,如果是,则继续加载管理函数:
define( 'ch3hmi', 1 );

if ( is_admin() ) {
    require plugin_dir_path( __FILE__ ) .
        'ch3-hide-menu-item-admin-functions.php';
}
  1. 保存并关闭插件文件。

  2. 虽然插件将继续像以前一样工作,但动作钩子注册代码只有在显示管理页面时才会被处理。

它是如何工作的...

is_admin 函数用于快速判断当前正在渲染的页面是否为管理页面。如果是,我们的插件代码使用标准的 PHP include 函数来加载和执行一个单独文件的内容。在这种情况下,该文件是位于插件目录中的第二个 PHP 文件。为了使插件文件的位置更加灵活,我们使用 WordPress 的 plugin_dir_path 函数构建包含管理功能的文件路径。

虽然将如此少的代码放在单独的文件中的好处很小,但这种方法在处理较大的管理面板时对性能的影响更大。除了不需要在每次页面加载时注册动作钩子外,PHP 解释器在渲染面向前端的页面时也不必确保该第二个文件内容的语法有效。

参见

  • 隐藏用户不应从默认菜单访问的项目 菜谱

在用户设置中存储样式表数据

虽然大多数常见的插件选项通常以简单的文本框、复选框或下拉列表的形式呈现给用户,但在某些情况下,需要存储更多文本以供用户设置。一个很好的例子是插件特定的样式表,它允许用户更改插件输出的视觉外观。虽然在本章的 将样式表加载到格式化插件输出 菜谱中加载单独的样式表文件效果很好,但这种方法并没有给用户太多自由来更改这些样式规则以更好地与他们的站点设计配合,因为用户对样式表所做的任何更改都会在通过 WordPress 插件升级过程更新插件时被覆盖。

解决这个问题的方法是将样式表数据与配置选项的其他部分一起存储在站点数据库中。这样,在升级时信息将保持完整。本菜谱展示了如何将上一章创建的插件更改为使用外部文件初始化插件选项,如何创建一个管理面板允许用户修改或重置样式表,以及如何使用新数据将样式信息输出到页面标题。本章学到的许多经验都将用于创建最终结果。

准备工作

你应该已经遵循了上一章中的 将样式表加载到格式化插件输出 菜谱,以便为本菜谱提供一个起点。或者,你可以在开始菜谱之前,从下载的代码包中获取结果代码(第二章/ch2-private-item-text/ch2-private-item-text-v2.php),并将文件重命名为 ch2-private-item-text.php

如何做到这一点...

  1. 导航到你的开发安装的 WordPress 插件目录中的 ch2-private-item-text 文件夹。

  2. 在文本编辑器中打开 ch2-private-item-text.php 文件。

  3. 添加以下代码行以实现激活回调,初始化插件选项,当它被安装或升级时:

register_activation_hook( __FILE__, 'ch2pit_get_options' );

function ch2pit_get_options() {
    $options = get_option( 'ch2pit_options', array() );

    $stylesheet_location = plugin_dir_path( __FILE__ ) . 
                               'stylesheet.css';
    $new_options['stylesheet'] = 
        file_get_contents( $stylesheet_location );

    $merged_options = wp_parse_args( $options, $new_options );
    $compare_options = array_diff_key( $new_options, $options );
    if ( empty( $options ) || !empty( $compare_options ) ) {
        update_option( 'ch2pit_options', $merged_options );
    }
    return $merged_options;
}
  1. 将以下代码段添加以注册一个在构建菜单时被调用的函数,以便在设置菜单下添加一个附加项:
add_action( 'admin_menu', 'ch2pit_settings_menu' );

function ch2pit_settings_menu() {
    add_options_page( 'Private Item Text Configuration',
         'Private Item Text', 'manage_options',
         'ch2pit-private-item-text', 'ch2pit_config_page' );
}
  1. 插入以下代码以渲染选项页面。

打印样式表的文本区域应该从新的一行开始,以避免样式表编辑器开头有额外的空格。此外,如果你从这本书的数字副本中复制代码,请确保不要丢失font-family中的连字符。

function ch2pit_config_page() {
    // Retrieve plugin configuration options from database
    $options = ch2pit_get_options(); ?>

    <div id="ch2pit-general" class="wrap">
    <h2>Private Item Text</h2>

    <!-- Code to display confirmation messages when settings
         are saved or reset -->
    <?php if ( isset( $_GET['message'] ) &&
               $_GET['message'] == '1' ) { ?>
        <div id='message' class='updated fade'><p>
            <strong>Settings Saved</strong></p></div>
    <?php } elseif ( isset( $_GET['message'] )
                     && $_GET['message'] == '2' ) { ?>
        <div id='message' class='updated fade'><p>
        <strong>Stylesheet reverted to original</strong></p></div>
    <?php } ?>

    <form name="ch2pit_options_form" method="post"
          action="admin-post.php"> 
    <input type="hidden" name="action"
           value="save_ch2pit_options" />
    <?php wp_nonce_field( 'ch2pit' ); ?>

    Stylesheet<br />
<textarea name="stylesheet" rows="10" cols="40" style="font-family:Consolas,Monaco,monospace"><?php echo esc_html ( $options['stylesheet'] ); ?></textarea><br />
    <input type="submit" value="Submit" class="button-primary" />
    <input type="submit" value="Reset" name="resetstyle"
           class="button-primary" />
    </form>
    </div>
<?php }
  1. 添加以下代码块以注册一个在用户选项保存时被调用的函数,并为该函数提供一个实现:
add_action( 'admin_init', 'ch2pit_admin_init' );

function ch2pit_admin_init() {
    add_action( 'admin_post_save_ch2pit_options',
                'process_ch2pit_options' );
}

function process_ch2pit_options() {
    // Check that user has proper security level
    if ( !current_user_can( 'manage_options' ) ) {
        wp_die( 'Not allowed' );
    }

    // Check if nonce field is present
    check_admin_referer( 'ch2pit' );

    // Retrieve original plugin options array
    $options = ch2pit_get_options();

    if ( isset( $_POST['resetstyle'] ) ) {
        $stylesheet_location = plugin_dir_path( __FILE__ ) . 
                                   'stylesheet.css';
        $options['stylesheet'] =
            file_get_contents( $stylesheet_location ); 
        $message = 2;
    } else {
        // Cycle through all fields and store their values
        // in the options array
        foreach ( array( 'stylesheet' ) as $option_name ) {
            if ( isset( $_POST[$option_name] ) ) {
                $options[$option_name] = $_POST[$option_name];
            }
        } 
        $message = 1;
    }

    // Store updated options array to database
    update_option( 'ch2pit_options', $options );

    // Redirect the page to the configuration form
    wp_redirect( add_query_arg(
                     array(
                         'page' => 'ch2pit-private-item-text',
                         'message' => $message ),
                         admin_url( 'options-general.php' ) ) );
    exit;
}
  1. 删除与函数ch2pit_queue_stylesheet关联的add_action函数调用,以及ch2pit_queue_stylesheet函数本身。

  2. 添加以下代码以将用户可修改的样式表代码添加到页面标题中:

add_action( 'wp_head', 'ch2pit_page_header_output' );

function ch2pit_page_header_output() { ?>
    <style type='text/css'>
    <?php
        $options = ch2pit_get_options();
        echo $options['stylesheet'];
    ?>
    </style>
<?php }
  1. 保存并关闭插件文件。

  2. 从管理界面停用并重新激活“第二章 - 私有项目文本”插件。

  3. 导航到设置菜单并选择“私有项目文本”子菜单项,以查看新创建的配置面板,其中包含提交更改到样式表或将其重置为其初始状态的功能,如下面的截图所示:

图片

  1. 访问网站并查看页面源代码,以查看在配置页面中输入的样式表数据出现在 HTML 头中:
<style type='text/css'>
.private {
    color: #6E6A6B;
}

.register {
    background-color: #ff4d4d;
    color: #fff;
    padding-left: 10px;
}
</style>

它是如何工作的...

重新使用本章中涵盖的许多元素,这个配方创建了一个简单而有效的配置界面,允许用户更改用于在帖子中突出显示私有文本的颜色,而不是将此颜色硬编码在插件文件中。

话虽如此,这个配方确实引入了两个新概念。第一个是通过从文件中读取数据来初始化插件选项,而不是将所有这些信息存储在 PHP 代码中。当处理具有大量内容(如样式表)的选项时,这种技术很有用。

下一个感兴趣的部分是在数据处理函数中,代码检查在重置样式表和提交用户更改以存储在网站数据库之间的哪个按钮被按下。根据结果,处理代码将要么从文件中读取初始样式表,要么使用用户提交的数据来更新配置数据。

除了这两个新概念之外,另一个主要变化是对输出引用外部样式表文件的头部代码的代码进行了修改。在这个新版本中,对直接将存储在选项表中的样式表内容输出到浏览器的内容进行了更改。

应该注意的是,本配方在将 CSS 代码添加到页面标题之前不会检查用户是否输入了有效的 CSS 代码,因为验证这一点目前可能过于复杂。可以根据需要使用库,如 CSSTidy([csstidy.sourceforge.net/](http://csstidy.sourceforge.net/))来完成此任务。

参见

  • 在第二章中,创建 一个 新的 包围 短代码的配方,插件框架基础

  • 在第二章中,加载样式表以格式化插件输出的配方,插件框架基础

从单个管理页面管理多套用户设置

在本章中,您已经学习了如何创建配置页面来管理我们插件的单套配置选项。在某些情况下,仅能指定单套选项可能不足以满足需求。例如,回顾上一章中创建的 Twitter 嵌入短代码插件,单个配置面板只会允许用户指定一套选项,例如所需的 Twitter 流尺寸或要显示的推文数量。

一个更灵活的解决方案是允许用户指定多套配置选项,然后可以通过额外的短代码参数(例如,[twitterfeed user_name="WordPress" option_id="2"])调用它们。

当您首先想到配置此类插件时,可能会想到创建一个具有子菜单的多级菜单项来存储多个不同的设置,但这种方法将为用户导航产生一个非常不自然的界面。更好的方法是使用单个面板,但给用户一个选择多个要修改的选项集的方法。

在本配方中,您将学习如何增强之前创建的 Twitter 流短代码插件,使其能够从插件配置面板控制嵌入流的宽度和要显示的推文数量,并使用户能够指定多个显示尺寸。

准备工作

您应该已经遵循了上一章中的创建带参数的新短代码配方,以便为本配方提供一个起点。或者,您可以从下载的代码包中获取结果代码(Chapter 2/ch2-twitter-embed/ch2-twitter-embed.php)。

如何操作...

  1. 导航到您开发安装的 WordPress 插件目录中的ch2-twitter-embed文件夹。

  2. 在文本编辑器中打开ch2-twitter-embed.php文件。

  3. 添加以下代码行以实现激活回调,以便在安装或升级时初始化插件选项:

register_activation_hook( __FILE__, 
                          'ch2te_set_default_options_array' );

function ch2te_set_default_options_array() {
    ch2te_get_options();
}

function ch2te_get_options( $id = 1 ) {
    $options = get_option( 'ch2te_options_' . $id, array() );

    $new_options['setting_name'] = 'Default';
    $new_options['width'] = 560;
    $new_options['number_of_tweets'] = 3;

    $merged_options = wp_parse_args( $options, $new_options );
    $compare_options = array_diff_key( $new_options, $options );
    if ( empty( $options ) || !empty( $compare_options ) ) {
        update_option( 'ch2te_options_' . $id, $merged_options );
    }
    return $merged_options;
}
  1. 插入以下代码段以注册一个在组装管理菜单时被调用的函数。当发生这种情况时,回调函数会将一个项目添加到设置菜单,并指定用于渲染配置页面的函数:
// Assign function to be called when admin menu is constructed
add_action( 'admin_menu', 'ch2te_settings_menu' );

// Function to add item to Settings menu and
// specify function to display options page content
function ch2te_settings_menu() {
    add_options_page( 'Twitter Embed Configuration',
                      'Twitter Embed', 'manage_options',
                      'ch2te-twitter-embed', 'ch2te_config_page' );
}
  1. 将以下代码添加以实现配置页面渲染函数:
// Function to display options page content 
function ch2te_config_page() { 
    // Retrieve plugin configuration options from database 
    if ( isset( $_GET['option_id'] ) ) {
        $option_id = intval( $_GET['option_id'] ); 
    } elseif ( isset( $_POST['option_id'] ) ) {
        $option_id = intval( $_POST['option_id'] ); 
    } else {
        $option_id = 1;
    }

    $options = ch2te_get_options( $option_id ); ?>  

    <div id="ch2te-general" class="wrap"> 
    <h2>Twitter Embed</h2> 

    <!-- Display message when settings are saved -->
    <?php if ( isset( $_GET['message'] ) &&
               $_GET['message'] == '1' ) { ?>
        <div id='message' class='updated fade'>
            <p><strong>Settings Saved</strong></p></div>
    <?php } ?>

    <!-- Option selector -->
    <div id="icon-themes" class="icon32"><br></div>
    <h2 class="nav-tab-wrapper">
    <?php for ( $counter = 1; $counter <= 5; $counter++ ) {
        $temp_options = ch2te_get_options( $counter ); 
        $class = ( $counter == $option_id ) ?
                 ' nav-tab-active' : ''; ?> 

    <a class="nav-tab<?php echo $class; ?>" href="<?php echo 
      add_query_arg( array( 'page' => 'ch2te-twitter-embed', 'option_id' => $counter ), admin_url(  'options-general.php' ) ); ?>"><?php echo $counter; ?><?php if ( $temp_options !== false ) echo ' (' .  $temp_options['setting_name'] . ')'; else echo ' (Empty)'; ?></a>
    <?php } ?>
    </h2><br />    

    <!-- Main options form --> 
    <form name="ch2te_options_form" method="post" 
          action="admin-post.php">     
    <input type="hidden" name="action"
           value="save_ch2te_options" /> 
    <input type="hidden" name="option_id" 
                         value="<?php echo $option_id; ?>" /> 
    <?php wp_nonce_field( 'ch2te' ); ?> 
    <table> 
        <tr><td>Setting name</td> 
            <td><input type="text" name="setting_name" 
      value="<?php echo esc_html( $options['setting_name'] ); ?>"/>
            </td>
        </tr> 
        <tr><td>Feed width</td> 
            <td><input type="text" name="width"
        value="<?php echo esc_html( $options['width'] ); ?>"/></td>
        </tr> 
        <tr><td>Number of Tweets to display</td> 
            <td><input type="text" name="number_of_tweets" value=
        "<?php echo esc_html( $options['number_of_tweets'] ); ?>"
            /></td>
        </tr>        
    </table><br /> 
    <input type="submit" value="Submit" class="button-primary" /> 
    </form> 
    </div> 
<?php }
  1. 将以下代码块添加到注册一个函数,该函数将在提交到网站时处理用户选项:
add_action( 'admin_init', 'ch2te_admin_init' ); 

function ch2te_admin_init() { 
    add_action( 'admin_post_save_ch2te_options',
                'process_ch2te_options' ); 
}
  1. 将以下代码添加以实现之前代码块中声明的process_ch2te_options函数:
// Function to process user data submission 
function process_ch2te_options() { 
    // Check that user has proper security level 
    if ( !current_user_can( 'manage_options' ) ) {
        wp_die( 'Not allowed' ); 
    }

    // Check that nonce field is present 
    check_admin_referer( 'ch2te' );

    // Check if option_id field was present  
    if ( isset( $_POST['option_id'] ) ) {
        $option_id = intval( $_POST['option_id'] ); 
    } else {
        $option_id = 1; 
    }

    // Build option name and retrieve options 
    $options = ch2te_get_options( $option_id ); 

    // Cycle through all text fields and store their values 
    foreach ( array( 'setting_name' ) as $param_name ) { 
        if ( isset( $_POST[$param_name] ) ) { 
            $options[$param_name] = sanitize_text_field(
                                        $_POST[$param_name] ); 
        } 
    }

    // Cycle through all numeric fields, convert to int and store
    foreach( array( 'width', 
                    'number_of_tweets' ) as $param_name ) { 
        if ( isset( $_POST[$param_name] ) ) { 
            $options[$param_name] = intval( $_POST[$param_name] ); 
        } 
    }

    // Store updated options array to database 
    $options_name = 'ch2te_options_' . $option_id; 
    update_option( $options_name, $options );

    $clean_address = add_query_arg( array( 'message' => 1,  
                         'option_id' => $option_id, 
                         'page' => 'ch2te-twitter-embed' ), 
                         admin_url( 'options-general.php' ) );
    wp_redirect( $clean_address ); 
    exit; 
}
  1. 找到ch2te_twitter_embed_shortcode函数,并按以下方式修改它以接受新的option_id参数并加载插件选项以生成所需的输出。配方中的更改以粗体显示:
function ch2te_twitter_embed_shortcode( $atts ) { 
    extract( shortcode_atts( array( 
                'user_name' => 'ylefebvre', 
                'option_id' => '1' 
           ), $atts ) );

 if ( intval( $option_id ) < 1 || intval( $option_id ) > 5 ) { 
 $option_id = 1; 
 }
    $options = ch2te_get_options( $option_id );    if ( !empty( $user_name ) ) {
        $output = '<a class="twitter-timeline" href="'; 
        $output .= esc_url( 'https://twitter.com/' . $user_name );
 $output .= '" data-width="' . $options['width'] . '" ';
 $output .= 'data-tweet-limit="';
        $output .= $options['number_of_tweets'];
        $output .= '">Tweets by ' . esc_html( $user_name );
        $output .= '</a><script async ';
        $output .= 'src="img/"';
        $output .= ' charset="utf-8"></script>';
    } else {
        $output = '';
    }
    return $output; 
}
  1. 保存并关闭插件文件。

  2. 从管理界面停用然后激活第二章 - Twitter 嵌入插件以执行其激活函数并创建默认设置。

  3. 导航到设置菜单并选择 Twitter 嵌入子菜单项,以查看新创建的配置面板,其中显示第一组选项,并且更多选项可通过页面顶部的下拉列表访问。

图片

  1. 要选择要使用的选项集,请将option_id参数添加到用于显示 Twitter 流的短代码中,如下所示:
[twitterfeed user_name="WordPress" option_id="1"]

它是如何工作的...

此配方展示了我们如何利用options数组通过动态创建options数组的名称来创建多组选项。在get_option函数调用的第一个参数中,我们不是使用特定的选项名称,而是创建一个带有选项 ID 的字符串。此 ID 作为配置页面上的 URL 参数和当处理表单数据时的隐藏文本字段发送。

代码的其余部分与我们在本章中看到的其他示例非常相似,因为访问数组元素的方式保持不变。

相关内容

  • 使用 HTML 渲染管理页面内容的配方

创建网络级别的管理页面

WordPress 的一个非常强大的功能是它能够从单个平台安装中运行多个网站。每个网站都可以有自己的内容和视觉标识,而管理员可以从一个仪表板管理所有网站。当您以多站网络运行 WordPress 时,它会在仪表板中添加一个额外的部分,用于网络级别管理。我们在此章中创建的所有插件都提供网站级别的配置面板,允许按站点定制配置选项。虽然这将是大多数插件所期望的行为,但对于某些配置元素可能更倾向于由网络级别管理员在单一位置设置并应用于网络中的所有网站。

在此配方中,您将学习如何修改我们的 Google Analytics 插件,使其在多站安装上以网络级别配置,在常规 WordPress 配置上以站点级别配置。

准备工作

你应该已经遵循了从操作和过滤器钩子访问用户设置的配方,并且生成的插件应该在你的开发站点中处于激活状态。或者,你可以从下载的代码包中获取生成的代码(Chapter 3/ch2-page-header-output/ch2-page-header-output-v9.php),在开始此配方之前将文件ch2-page-header-output-v9.php重命名为ch2-page-header-output.php。你还应该能够访问配置为网络模式的 WordPress 安装。你可以在codex.wordpress.org/Create_A_Network上了解更多关于配置 WordPress 以运行多站点的步骤。

如何做到这一点...

  1. 导航到你的开发安装的 WordPress 插件目录中的ch2-page-header-output文件夹。

  2. 在文本编辑器中打开ch2-page-header-output.php文件。

  3. 修改ch2pho_get_options函数的实现,用网络级别的版本替换两个选项函数。代码部分显示了需要更改的带有粗体显示的两行:

$options = get_site_option( 'ch2pho_options', array() );
 update_site_option( 'ch2pho_options', $merged_options );
  1. 定位到添加回调以填充管理菜单的add_action函数调用,并在注册回调之前添加代码以检查安装是单站点还是多站点。以下代码显示了带有粗体的新元素:
if ( is_multisite() ) {
 add_action( 'network_admin_menu', 'ch2pho_settings_menu' );
} else {
    add_action( 'admin_menu', 'ch2pho_settings_menu' );
}
  1. 修改ch2pho_settings_menu函数,根据网站是普通站点还是带有以下粗体显示的新部分的 multisite,向不同的菜单中添加新项目:
function ch2pho_settings_menu() {
 if ( is_multisite() ) {
 $options_page = add_submenu_page( 'settings.php',
 'My Google Analytics Configuration', 
            'My Google Analytics',
 'manage_options', 'ch2pho-my-google-analytics',
            'ch2pho_config_page' );
 } else {
 $options_page = add_submenu_page( 'options-general.php',
 'My Google Analytics Configuration',
            'My Google Analytics',
 'manage_options', 'ch2pho-my-google-analytics',   
            'ch2pho_config_page' );
 } 
    if ( !empty( $options_page ) ) {
        add_action( 'load-' . $options_page, 'ch2pho_help_tabs' ); 
    }
}
  1. process_ch2pho_options函数中,将update_option的调用替换为update_site_option的调用:
update_site_option( 'ch2pho_options', $options ); 
  1. 仍然在process_ch2pho_options函数中,在wp_redirect函数调用周围进行以下更改,带有以下粗体显示的新元素:
if ( is_multisite() ) {
 $redirect_page = '/network/settings.php';
} else {
 $redirect_page = 'options-general.php';
} 
wp_redirect( add_query_arg( 
             array( 'page' => 'ch2pho-my-google-analytics', 
                    'message' => '1' ), 
             admin_url( $redirect_page ) ) );
  1. 修改ch2pho_config页面函数,以修改表单操作,使其在多站点安装中找到带有以下粗体显示的新代码的admin-post.php
<form method="post"
      action="<?php echo admin_url( 'admin-post.php' ); ?>">
  1. 保存并关闭插件文件。

  2. 在 WordPress 的网络安装中,访问网络管理中的插件部分。

  3. 网络激活Chapter 2 - Page Header Output插件。你会看到现在在设置菜单下有一个新项目可用。

图片

  1. 访问网络中的任何站点,查看页面源代码以查看 Google Analytics 代码是否已添加到页面标题中,并且用户账户已配置在我们的管理页面上。

它是如何工作的...

当创建网络级别的插件时,is_multisite函数非常有用,可以确定站点是否配置为多站点。在配方中,我们使用它来注册在构建不同菜单时(常规单站点管理菜单和网络管理菜单)要调用的回调。我们还使用is_multisite函数来注册实际的菜单项,并确定在保存选项后用户应重定向到何处。

这些修改中其他有用的功能包括 get_site_optionupdate_site_option。当在多站点配置中调用时,这些函数将从数据库中检索网络级别的选项。或者,当在常规的单站点上运行时,它们将默认访问站点级别的选项。通过在我们的更新代码中使用这些函数,我们使所有与站点选项的交互都兼容单站点和多站点安装。

这个菜谱的最后一个元素与我们在网络级别激活此插件的方式有关。这意味着它将在网络中创建的所有站点中都是活跃的。更具体地说,在我们的插件案例中,这意味着所有站点都将输出谷歌分析代码到它们的头部,相关账户在网络上配置时指定一次。

参见

  • 从操作和过滤器钩子访问用户设置 菜谱

第四章:自定义帖子类型的强大之处

本章通过以下主题涵盖了 WordPress 最强大的功能之一:自定义帖子类型:

  • 创建自定义帖子类型

  • 向自定义帖子类型编辑器添加新部分

  • 使用自定义布局显示单个自定义帖子类型项

  • 在短代码中显示自定义帖子类型数据

  • 为自定义帖子类型添加自定义分类

  • 向分类添加自定义字段

  • 从自定义帖子类型编辑器中隐藏分类编辑器

  • 在自定义帖子列表页面显示额外列

  • 在自定义帖子列表页面添加自定义分类过滤器

  • 为自定义分类添加快速编辑字段

  • 使用插件过滤器更新页面标题以包含自定义帖子数据

简介

建立在开放性和易用性的历史基础上,WordPress 在引入自定义帖子类型时达到了定制的新的高度。

自定义帖子类型是通过使用 WordPress API 创建的新类别项目,它们在 WordPress 管理界面中作为全新的部分出现,位于默认的帖子(Posts)和页面(Pages)部分旁边。这些自定义项目可以用来存储任何类型的信息,包括事件、错误报告、食谱、电影评论等等。

当使用自定义帖子类型来实现此类功能时,开发者可以利用 WordPress 内部内容编辑功能,包括其强大的文本编辑器和用户友好的媒体上传器。自定义帖子类型还简化了开发者的数据管理,因为这些新条目相关的所有信息都使用现有的表结构存储在网站数据库中。最后,自定义帖子类型可以利用已建立的主题和模板系统来显示网站管理员存储在这些新内容类型中的信息。

如果你曾经窥视过 WordPress 网站背后的 MySQL 数据库,你就会知道帖子(posts)、页面(pages)、附件(attachments)、修订(revisions)和导航菜单项共享相同的表。本质上,所有这些数据元素都是自定义帖子类型,其中一些使用标准文本编辑器,而其他,如导航菜单,则有一个自定义管理界面。这些项目类型中的每一个也有不同的机制在网站上显示。

使用自定义帖子类型可以打开无限的可能性来定制 WordPress 安装的功能,并为最终用户提供定制解决方案,而无需投入大量时间重新发明轮子。本章通过创建一个书评系统,涵盖了创建自定义帖子类型的所有方面,包括创建新类型的元素,在网站上显示新存储的信息,以及定制环境以创建具有独特功能的编辑器。

创建自定义帖子类型

自定义文章类型的初始创建非常简单。它只需要从动作钩子回调中调用一个函数。一旦设置好,许多功能立即对管理员和网站访客可用。本菜谱展示了如何创建一个新自定义文章类型,该类型将用于存储书籍评论。

准备工作

您应该能够访问一个 WordPress 开发环境,无论是在您的本地计算机上还是在远程服务器上,您将能够加载您的新插件文件。

如何实现...

  1. 导航到您的开发安装的 WordPress 插件目录。

  2. 创建一个名为ch4-book-reviews的新目录。

  3. 导航到该目录并创建一个名为ch4-book-reviews.php的新文本文件。

  4. 在代码编辑器中打开新文件,并在插件文件顶部添加一个适当的标题,命名为第四章 - 书籍评论

  5. 添加以下代码行以注册一个函数,该函数将在 WordPress 生成页面时的初始化阶段执行:

add_action( 'init', 'ch4_br_create_book_post_type' ); 
  1. 添加以下代码块以提供ch4_br_create_book_post_type函数的实现:
function ch4_br_create_book_post_type() { 
    register_post_type( 'book_reviews', 
        array( 
            'labels' => array( 
                'name' => 'Book Reviews', 
                'singular_name' => 'Book Review', 
                'add_new' => 'Add New', 
                'add_new_item' => 'Add New Book Review', 
                'edit' => 'Edit', 
                'edit_item' => 'Edit Book Review', 
                'new_item' => 'New Book Review', 
                'view' => 'View', 
                'view_item' => 'View Book Review', 
                'search_items' => 'Search Book Reviews', 
                'not_found' => 'No Book Reviews found', 
                'not_found_in_trash' =>  
                    'No Book Reviews found in Trash', 
                'parent' => 'Parent Book Review'                     
            ), 
            'public' => true, 
            'menu_position' => 20, 
            'supports' =>  
                array( 'title', 'editor', 'comments', 
                       'thumbnail', 'custom-fields' ), 
            'taxonomies' => array( '' ), 
            'menu_icon' =>  
                plugins_url( 'book-reviews.png', __FILE__ ), 
            'has_archive' => true,
            'exclude_from_search' => true 
        ) 
    ); 
} 
  1. 保存并关闭插件文件。

  2. 从网站,例如 IconArchive (www.iconarchive.com),找到并下载一个 24 x 24 像素的 PNG 格式书籍图标,将其调整大小为 20 x 20 像素,并将其保存为book-reviews.png到插件目录中。

  3. 导航到插件管理页面并激活第四章 - 书籍评论插件。

  4. 点击新出现的“书籍评论”菜单项,位于页面部分下,以查看书籍评论创建和管理界面。

图片

  1. 点击标题旁边的“添加新内容”按钮,以显示具有完整 WordPress 文本编辑器、自定义字段编辑器、评论控制、发布控制和特色图片部分的书籍评论编辑器。

  2. 通过指定书籍评论标题(例如,WordPress 插件开发秘籍)和简短描述来填写新条目。

  3. 滚动到“自定义字段”部分,将字段的名称输入为book_author,其值为Yannick Lefebvre。点击“添加自定义字段”以创建第二个字段。

如果您的 WordPress 安装中已经存在一些自定义字段(来自其他插件的数据输入),您需要点击“输入新内容”才能将名称设置为book_author

  1. 将第二个字段的名称设置为book_rating,其值为5

  2. 从网站,例如 Google 图片 (images.google.com) 或 Packt (www.packtpub.com),找到并下载书籍封面图片。

  3. 点击编辑界面右侧侧边栏中的“设置特色图片”链接。

  4. 点击“选择文件”以选择您下载到计算机上的图片,并让 WordPress 将其上传到您网站的wp-content/uploads文件夹。

  5. 一旦文件上传并且 WordPress 显示其信息,请查看媒体上传对话框的底部并点击“设置特色图片”链接,将其与您正在创建的图书评论关联。

图片

  1. 点击“发布”按钮以保存第一个图书评论。

  2. 访问设置菜单中的“永久链接”部分,并点击“保存更改”按钮(不更改您的永久链接设置)。

  3. 返回您创建的图书评论,并点击“查看图书评论”按钮,在您的网页浏览器中查看新创建的内容。

它是如何工作的...

通过调用register_post_type函数,整个 WordPress 环境都会意识到这个新帖子类型的存在。这种意识包括创建一个专门的区域来创建和编辑此类型的帖子,以及处理图书评论的网页请求的能力。

如本食谱开头所述,该函数非常简单易用,只需两个参数:

register_post_type( $post_type, $args ); 

第一个参数是一个表示帖子类型的文本字符串。请注意,在选择此名称时,它将用作所有使用新类型的项目的默认永久链接值,并且它应该足够独特,以避免与其他插件发生潜在冲突。

第二个参数是一个属性数组,它指定了新帖子类型的特点并决定了如何编辑此类型。

在这个特定示例中,属性数组的第一个元素实际上是一个数组,它包含了一些标签。这些标签指示在管理在新帖子类型下创建的项目时应显示的文本字符串。例如,如果我们查看步骤 11 之前的截图,消息“未找到图书评论”直接来自此数组中not_found标签的定义。

第二个参数名为public,它决定了帖子类型的行政界面是否应该显示以管理它,以及访客是否能够查看单个项目。接下来是配置数组中的menu_position成员,它表示新元素在行政菜单中的期望位置。在这个例子中,值 20 表示它应该显示在“页面”菜单项之后。访问 WordPress Codex(codex.wordpress.org/Function_Reference/register_post_type)以获取此参数所有潜在值及其相关位置的完整列表。

supports参数是另一个数组,它指示对于使用自定义帖子类型的项,内容编辑器应显示哪些部分。在这种情况下,我们省略了一些部分,如“作者”、“摘要”、“引用”、“修订”、“页面属性”和“帖子格式”,因为它们对于图书评论来说不是必需的。

配置数组中的下几个参数表示我们目前不想定义自定义分类法,并指定在管理菜单中显示在帖子类型名称旁边的图像文件的路径和名称。最后,最后两个参数确定当用户访问网站的 /book_reviews 页面时,WordPress 是否应该为新的类型提供一个存档列表页面,以及书籍评论是否应该从搜索结果中排除。

实际上,还有许多其他参数可以包含在配置数组中,以更精确地控制新自定义帖子类型的一些方面。请访问 WordPress Codex (codex.wordpress.org/Function_Reference/register_post_type) 了解更多相关信息。

还有更多...

虽然默认情况下使用内部帖子类型名称来生成帖子永久链接,但实际上可以覆盖它以创建更美观的 URL。

修改自定义帖子类型永久链接的别名

自定义帖子类型配置的可选成员是 rewrite 参数。它可以定义如下:

'rewrite' => array( 'slug' => 'awesome-book-reviews' ) 

虽然这看起来可能非常简单,但只有在您进入“永久链接”部分并保存更改后,永久链接才会更改,就像我们在食谱中所做的那样。或者,您可以从 WordPress 重写模块中调用程序来请求重新构建永久链接配置。由于这不是每次 WordPress 显示页面时都应该做的事情,而且在插件初始化或升级时做这件事可能太早了,所以一个好的调用这些函数的地方是在插件选项存储函数中。您甚至可以决定在插件配置页面上允许管理员指定他们自己的别名。重置永久链接规则的代码如下:

global $wp_rewrite; 
$wp_rewrite->flush_rules(); 

在自定义帖子类型编辑器中添加新部分

虽然到目前为止已经实施的自定义帖子编辑器是功能性的,但它并不是最友好的用户界面,尤其是在自定义字段部分,用户在创建新项目时需要输入或选择每个字段的名称。一种更干净的方法是使用我们在上一章中看到的元框机制创建一个自定义界面,以显示与书籍评论相关联的所有数据。

这个食谱展示了如何创建一个与自定义帖子类型相关联的元框,以及如何保存在新界面中输入的信息。

准备工作

您应该已经遵循了 创建自定义文章类型 的配方,以便为本配方提供一个起点,并且生成的插件仍然在您的开发站点中处于活动状态。或者,您可以从 Packt 网站下载的代码包(www.packtpub.com/support)中获取生成的代码(Chapter 4/ch4-book-reviews/ch4-book-reviews-v1.php),并将文件重命名为 ch4-book-reviews.php

如何做到这一点...

  1. 导航到开发安装的 WordPress 插件目录中的 ch4-book-reviews 文件夹。

  2. 在代码编辑器中打开 ch4-book-reviews.php 文件。

  3. 在现有函数之后添加以下代码行以注册一个在访问管理界面时调用的函数:

add_action( 'admin_init', 'ch4_br_admin_init' ); 
  1. 将以下代码段添加以提供 ch4_br_admin_init 函数的实现并注册一个与 book_reviews 文章类型关联的元框:
function ch4_br_admin_init() { 
    add_meta_box( 'ch4_br_review_details_meta_box',  
                  'Book Review Details', 
                  'ch4_br_display_review_details_meta_box', 
                  'book_reviews', 'normal', 'high' ); 
} 
  1. 插入此函数以实现 ch4_br_display_review_details_meta_box 函数并渲染元框内容:
function ch4_br_display_review_details_meta_box( $book_review ) {  
    // Retrieve current author and rating based on review ID 
    $book_author = 
          esc_html( get_post_meta( $book_review->ID, 
                                   'book_author', true ) ); 
    $book_rating = 
          intval( get_post_meta( $book_review->ID,
                                 'book_rating', true ) ); 
    ?> 
    <table> 
        <tr> 
            <td style="width: 100%">Book Author</td> 
            <td><input type="text" size="80" 
                       name="book_review_author_name" 
                       value="<?php echo $book_author; ?>" /></td> 
        </tr> 
        <tr> 
            <td style="width: 150px">Book Rating</td> 
            <td> 
                <select style="width: 100px" 
                        name="book_review_rating"> 
                <!-- Loop to generate items in dropdown list -->
                <?php
                for ( $rating = 5; $rating >= 1; $rating -- ) { ?> 
                <option value="<?php echo $rating; ?>"
                <?php echo selected( $rating, $book_rating ); ?>>
                <?php echo $rating; ?> stars 
                <?php } ?> 
                </select> 
            </td> 
        </tr> 
    </table> 
 <?php } 
  1. 添加以下代码段以注册一个在将帖子保存到数据库时调用的函数:
add_action( 'save_post', 'ch4_br_add_book_review_fields', 10, 2 ); 
  1. 为之前 add_action 调用中定义的 ch4_br_add_book_review_fields 函数添加实现:
function ch4_br_add_book_review_fields( $book_review_id,
                                        $book_review ) { 
    // Check post type for book reviews 
    if ( 'book_reviews' == $book_review->post_type ) { 
        // Store data in post meta table if present in post data 
        if ( isset( $_POST['book_review_author_name'] ) ) {
            update_post_meta( $book_review_id, 'book_author', 
                sanitize_text_field( 
                    $_POST['book_review_author_name'] ) ); 
        } 

        if ( isset( $_POST['book_review_rating'] ) && 
             !empty( $_POST['book_review_rating'] ) ) {            
            update_post_meta( $book_review_id, 'book_rating', 
                intval( $_POST['book_review_rating'] ) ); 
        } 
    } 
} 
  1. 在开发安装的 WordPress 插件目录的 ch4-book-reviews 文件夹中找到 ch4_br_create_book_post_type 函数,其中最初创建了新的书籍类型,并从 supports 数组中删除 custom-fields 元素:
   'supports' => array( 'title', 'editor', 'comments', 'thumbnail' ), 
  1. 保存并关闭插件文件。

  2. 打开之前创建的图书评论,查看新的图书评论详情元框,其中包含一个文本字段用于指定作者和一个下拉列表用于评分:

图片

它是如何工作的...

此配方使用 WordPress 内置的元框系统创建一个干净的界面,允许用户管理特定于自定义文章类型的字段,而无需使用繁琐的默认自定义字段编辑器。正如我们在第三章,“用户设置和管理页面”中看到的,可以使用 add_meta_box 函数创建自定义元框。除了声明元框并将其与自定义文章类型关联外,add_meta_box 还定义了一个回调,负责渲染框的内容。

配方的下一部分实现了渲染元框内容的函数。正如我们所见,这个框接收一个包含正在显示在文章编辑器中的书评信息的对象变量。使用这个对象,我们的代码检索文章 ID,并使用它来查询网站数据库以获取与条目关联的书籍作者和评分。一旦从数据库中检索到自定义字段数据,就可以用于在屏幕上渲染作者和评分字段。当创建新的书评时,对 get_post_meta 的两次调用都将返回空字符串,导致显示一个空文本字段和下拉列表中的最后一个条目。

get_post_meta 函数用于检索存储在文章编辑器自定义字段部分的已存储数据,并具有三个参数:

get_post_meta( $post_id, $field_name, $single ); 

第一个参数是文章 ID,这个 ID 可以通过使用 get_the_ID() 模板函数轻松获取。这个 ID 用于识别与自定义信息关联的文章。第二个参数是自定义字段名称,它应该与在文章编辑器中创建时指定的名称相匹配。第三个和最后一个参数表示返回值应该是单个值还是值的数组。如果设置为 false,即使自定义字段只包含单个值,它也会生成包含单个元素的数组。在大多数情况下,应该将其设置为 true 以接收可以直接访问的单个值。

此配方的最后几个步骤负责注册一个函数,当网站管理员保存或删除所有类型的文章时,该函数将被调用。由于它将处理所有类型的数据,保存回调必须首先检查接收到的文章数据的类型。如果是书评,代码将接着检查接收到的数据是否有效,并将信息存储在文章元数据表中。在此配方中,update_post_meta 函数的参数与 get_post_meta 函数类似,除了第三个参数,它用于指定要存储的数据。

关于此配方需要提到的最后一个细节是,在将回调关联到 save_post 动作钩子时使用 add_action 函数的第四个参数。此参数表示注册的回调将接收两个参数。如果此参数未设置,回调函数将永远不会收到那第二份数据。

参见

  • 创建自定义文章类型 的配方

  • 在第三章 用户设置和管理页面 中的 使用元框格式化管理页面 配方 [0346c3c6-27ee-45fb-bfd6-df398e04b2b4.xhtml]

使用自定义布局显示单个自定义文章类型项

当显示我们在新自定义帖子类型中创建的条目时,我们当前网站主题提供的默认布局可能无法始终愉快地显示其中包含的信息。在大多数情况下,您将能够看到主要帖子内容,但看不到与帖子关联的任何自定义字段数据。

本配方展示了如何创建一个自定义布局来显示我们在上一个配方中创建的书籍评论中存储的所有元素。

准备工作

您应该已经遵循了向自定义帖子类型编辑器添加新部分的配方,以便为本配方提供一个起点,并且生成的插件仍然在您的开发站点中处于活动状态。或者,您可以从下载的代码包中获取生成的代码(Chapter 4/ch4-book-reviews/ch4-book-reviews-v2.php),并将文件重命名为ch4-book-reviews.php

如何操作...

  1. 导航到您的开发安装的 WordPress 插件目录中的ch4-book-reviews文件夹。

  2. 在代码编辑器中打开ch4-book-reviews.php文件。

  3. 在注册一个函数以在 WordPress 决定使用哪个主题模板来渲染内容时,在现有函数之后添加以下代码行:

add_filter( 'template_include', 'ch4_br_template_include', 1 ); 
  1. 将以下代码部分添加到为ch4_br_template_include函数提供一个实现的代码部分:
function ch4_br_template_include( $template_path ) {      
    if ( 'book_reviews' == get_post_type() ) { 
        if ( is_single() ) { 
            // checks if the file exists in the theme first, 
            // otherwise install content filter 
            if ( $theme_file = locate_template( array
                 ( 'single-book_reviews.php' ) ) ) { 
                $template_path = $theme_file; 
            } else { 
                add_filter( 'the_content',
                            'ch4_br_display_single_book_review',
                            20 ); 
            } 
        } 
    } 
    return $template_path; 
} 
  1. 将以下代码部分添加到实现ch4_br_display_single_book_review函数以显示书籍评论,包括它们的自定义字段:
function ch4_br_display_single_book_review( $content ) {
    if ( !empty( get_the_ID() ) ) {
        // Display featured image in right-aligned floating div
        $content = '<div style="float: right; margin: 10px">';
        $content .= get_the_post_thumbnail( get_the_ID(),
                                            'medium' );
        $content .= '</div>';

        $content .= '<div class="entry-content">'; 

        // Display Author Name
        $content .= '<strong>Author: </strong>';
        $content .= esc_html( get_post_meta( get_the_ID(),
                                             'book_author', 
                                             true ) );
        $content .= '<br />';

        // Display yellow stars based on rating -->
        $content .= '<strong>Rating: </strong>';

        $nb_stars = intval( get_post_meta( get_the_ID(),
                                           'book_rating', 
                                           true ) );

        for ( $star_counter = 1; $star_counter <= 5; 
                  $star_counter++ ) {
            if ( $star_counter <= $nb_stars ) {
                $content .= '<img src="img/>                                         __FILE__ ) . '" 
                            />';
            } else {
                $content .= '<img src="img/>                            . '" />';
            }
        }

        // Display book review contents
        $content .= '<br /><br />';
        $content .= get_the_content( get_the_ID() );
        $content .= '</div>';

        return $content;
    }
} 
  1. 保存并关闭插件文件。

  2. 从像 IconArchive(www.iconarchive.com)这样的网站找到一个 32 x 32 像素的 PNG 格式像素星形图标,并将其保存为star-icon.png在插件目录中。

  3. 使用任何图形处理工具(例如,位于www.xnview.com/en/的免费多平台 XnViewMP 工具)创建星形图标的灰度版本,并将其保存为star-icon-grey.png

  4. 前往书籍评论管理页面,并点击之前配方中创建的现有条目下的查看链接,以查看使用新模板渲染的内容。

图片

它是如何工作的...

在渲染任何网页时,WordPress 的默认功能是在当前主题目录中搜索适用于当前内容的适用模板。在单个自定义帖子类型项的情况下,例如书籍评论,它首先寻找一个名为single-<post-type-name>.php的单个项模板,其中后一部分是实际的帖子类型名称。如果找不到此文件,它将默认使用通用单个项模板。在本章的第一个配方中,用于显示书籍评论的模板是默认的单个项模板,简单地命名为single.php

为了更好地支持我们新的文章类型,此配方将一个函数与template_include过滤器钩子关联起来,以改变该行为。更具体地说,我们使用locate_template函数来检查用户是否在主题目录中为book_reviews文章类型提供了一个模板。如果没有找到模板,我们将注册一个过滤器来用我们自己的布局覆盖页面内容。这使用户能够灵活地使用我们预定义的布局或提供他们自己的。

配方的其余部分实现了我们的 Book Review 内容的后备过滤器函数。此代码使用了多个 WordPress 模板函数,如get_the_ID()get_the_content(),以及get_post_meta函数,以显示当前项目的各种元素,包括书籍作者和其评分,以及主要文章内容和特色图像。

为了帮助用户为您的自定义文章类型构建自己的主题模板,您应该在插件文档中提供代码片段,展示如何检索您的自定义文章类型的自定义字段。

参见

  • 创建自定义文章类型的配方

在短代码中显示自定义文章类型数据

为了帮助访客通过我们使用的新自定义文章类型添加的项目进行导航,我们需要显示网站上所有书评的列表,以及导航元素以便处理大量项目。虽然 WordPress 提供了一个内置机制以存档页的形式列出文章项目,但插件很难以一致的方式修改所有可能的用户主题中结果页面的布局。更好的解决方案是创建一个短代码,该短代码可以在用户选择的任何位置显示一个或多个文章,包括页面、文章,甚至网站的前页。

此配方展示了如何创建一个短代码,该短代码可以一次检索并显示五个书评,并附带导航链接。

准备工作

您应该已经遵循了使用自定义布局显示单个自定义文章类型项目的配方,以便为此配方提供一个起点,并且生成的插件仍然在您的开发站点中处于活动状态。或者,您可以从下载的代码包中获取生成的代码(Chapter 4/ch4-book-reviews/ch4-book-reviews-v3.php)并将文件重命名为ch4-book-reviews.php

如何操作...

  1. 导航到您开发安装的 WordPress 插件目录中的ch4-book-reviews文件夹。

  2. 在代码编辑器中打开ch4-book-reviews.php文件。

  3. 在现有函数之后添加以下代码行,以注册一个声明新短代码的函数:

add_shortcode( 'book-review-list', 'ch4_br_book_review_list' ); 
  1. 将以下代码部分添加到为ch4_br_book_review_list函数提供实现的代码:
function ch4_br_book_review_list() { 
    // Preparation of query array to retrieve 5 book reviews 
    $query_params = array( 'post_type' => 'book_reviews', 
                           'post_status' => 'publish', 
                           'posts_per_page' => 5 );

    // Execution of post query 
    $book_review_query = new WP_Query; 
    $book_review_query->query( $query_params );

    // Check if any posts were returned by the query 
    if ( $book_review_query->have_posts() ) { 
        // Display posts in table layout 
        $output = '<table>';

        $output .= '<tr><th style="width: 350px"><strong>'; 
        $output .= 'Title</strong></th>'; 
        $output .= '<th><strong>Author</strong></th></tr>';

        // Cycle through all items retrieved 
        while ( $book_review_query->have_posts() ) { 
            $book_review_query->the_post();

            $output .= '<tr><td><a href="' . get_permalink(); 
            $output .= '">'; 
            $output .= get_the_title( get_the_ID() ) . '</a></td>'; 
            $output .= '<td>'; 
            $output .= esc_html( get_post_meta( get_the_ID(), 
                                                'book_author',
                                                true ) ); 
            $output .= '</td></tr>'; 
        }

        $output .= '</table>'; 

        // Display page navigation links 
        if ( $book_review_query->max_num_pages > 1 ) { 
            $output .= '<nav id="nav-below">'; 
            $output .= '<div class="nav-previous">'; 
            $output .= get_next_posts_link ( 
                '<span class="meta-nav">&larr;</span>' .
                ' Older reviews',
                $book_review_query->max_num_pages ); 
            $output .= '</div>'; 
            $output .= '<div class="nav-next">'; 
            $output .= get_previous_posts_link( 
                  'Newer reviews ' .
                  '<span class="meta-nav">&rarr;</span>', 
                  $book_review_query->max_num_pages ); 
            $output .= '</div>'; 
            $output .= '</nav>'; 
        }

        // Reset post data query 
        wp_reset_postdata(); 
    }

    return $output; 
} 
  1. 保存插件文件。

  2. 创建一个新页面并插入短代码[book-review-list]

  3. 发布并查看页面,以查看书评列表将显示在短代码的位置。

图片

  1. 如果系统中存在超过五个书评,请点击显示的导航链接。您会看到浏览器地址栏中的 URL 发生了变化,但条目列表显示的仍然是之前的前五个项目。

  2. 回到ch4-book-reviews.php文件,在ch4_br_book_review_list的顶部附近添加以下突出显示的代码,在初始化$query_params变量值的行之后:

// Preparation of query string to retrieve 5 book reviews 
$query_params = array( 'post_type' => 'book_reviews', 
                       'post_status' => 'publish', 
                       'posts_per_page' => 5 ); 

// Retrieve page query variable, if present 
$page_num = ( get_query_var( 'paged' ) ? 
              get_query_var( 'paged' ) : 1 ); 

// If page number is higher than 1, add to query array 
if ( $page_num != 1 ) {
 $query_params['paged'] = $page_num; 
}
  1. 保存并关闭插件文件。刷新包含我们新短语的页面,并使用导航链接查看项目列表是否已正确更改。

它是如何工作的...

如我们在第三章“用户设置和管理页面”中看到的,短代码是可以插入任何页面和文章中的文本元素,当它们被找到时,将被插件生成的内容所替换。注册的回调函数必须在执行结束时准备输出并将其作为返回值发送回去。

ch4_br_book_review_list函数的第一部分负责准备一个查询数组,以便传递给WP_Query类的新实例。这个类允许开发者轻松地从网站数据库的文章表中提取信息。在这个例子中,正在设置的查询参数包括内部文章类型名称(post_type)、我们想要显示的项目状态(post_status)以及每次应该检索的项目数量(posts_per_page)。

一旦查询字符串就绪,我们创建一个名为book_review_query的全局变量,并将其分配给一个WP_Query对象的新实例。一旦创建,我们使用刚刚组装的查询字符串初始化它。如果对象找到了文章,我们输出 HTML 代码来创建一个表格,并使用while循环遍历找到的所有项目,并使用类似于前两个菜谱的代码显示它们的标题和作者。

作为这个菜谱的一部分,我们看到了如果自定义文章类型的条目数量超过了posts_per_page查询参数指定的值,会在条目表下方添加导航控件,但由于我们手动创建了查询字符串,所以它们将不会正确工作。为了纠正这种情况,我们使用get_query_var函数来查看是否请求了页码。如果是这种情况,并且页码不是1,我们将该数字添加到我们的查询参数中。

还有更多...

如本菜谱开头所述,可能存在需要将自定义文章类型的项目列表作为主题模板的一部分显示的情况。以下部分展示了如何将短代码内容作为模板文件的一部分显示。

do_shortcode 函数

do_shortcode函数可以从任何主题模板文件中调用,用于主页或网站的任何其他部分,以渲染与短代码相关的内容。它接受一个参数,即包含任何参数的短代码字符串。要显示本食谱中创建的内容,我们只需调用以下代码:

<?php echo do_shortcode( '[book-review-list]' ); ?> 

为自定义帖子类型添加自定义分类

为了在网站上保持项目组织,管理员通常会使用内置的 WordPress 分类和术语来识别类似的项目。回顾一下我们在本章中迄今为止设置的图书评论系统,一种有用的分类类型是图书类型(例如,科幻小说、纪录片、小说、诗歌等)。

本食谱展示了如何创建一个新的分类(在 WordPress 后端中称为分类法)并将其与图书评论自定义帖子类型关联。

准备工作

您应该已经遵循了在短代码中显示自定义帖子类型数据的食谱,以为本食谱提供一个起点,并且生成的插件仍然在您的开发站点中处于活动状态。或者,您可以从下载的代码包中获取生成的代码(Chapter 4/ch4-book-reviews/ch4-book-reviews-v4.php),并将文件重命名为ch4-book-reviews.php

如何操作...

  1. 导航到您开发安装中 WordPress 插件目录的ch4-book-reviews文件夹。

  2. 在代码编辑器中打开ch4-book-reviews.php文件。

  3. 找到ch4_br_create_book_post_type函数,并在现有register_post_type调用之后添加以下代码以创建新的分类法:

register_taxonomy( 
    'book_reviews_book_type',       
    'book_reviews',                  
    array( 
        'labels' => array( 
            'name' => 'Book Type', 
            'add_new_item' => 'Add New Book Type', 
            'new_item_name' => 'New Book Type Name'
        ), 
        'show_ui' => true, 
        'show_tagcloud' => false, 
        'hierarchical' => true 
    )
); 
  1. 保存并关闭插件文件。

  2. 打开之前创建的图书评论,查看在帖子编辑器右侧新添加的图书类型元框。

  3. 点击+添加新图书类型链接创建一个新项目并将其指定为当前项目的类型。点击帖子编辑器右上角的更新按钮以保存评论:

图片

  1. 查看左侧管理菜单,您会看到添加了一个新的菜单项来管理图书类型,链接到一个类似于帖子和页面分类编辑器的编辑器:

图片

  1. 在插件文件中,在显示评分的部分之后添加以下代码到ch4_br_display_single_book_review函数中,以显示图书类型:
$book_types = wp_get_post_terms( get_the_ID(), 
                                'book_reviews_book_type' ); 

$content .= '<br /><strong>Type: </strong>';

if ( $book_types ) { 
    $first_entry = true; 
    for ( $i = 0; $i < count( $book_types ); $i++ ) { 
        if ( !$first_entry ) {
            $content .= ', ';
        }
        $content .= $book_types[$i]->name; 
        $first_entry = false; 
    } 
} else {
    $content .= 'None Assigned';
}
  1. 保存并关闭模板文件。

  2. 访问图书评论页面,查看在评分下方显示的图书类型。

它是如何工作的...

register_taxonomy函数用于在 WordPress 中创建新的分类类型并将其关联到帖子类型。它有三个参数:

register_taxonomy( $taxonomy_name, $post_type, $options );  

第一个参数是分类法的唯一标识符。第二个参数是它应该关联的帖子类型,这应该与register_post_type函数中声明的类型相匹配。第三个参数是一个参数数组,它决定了新分类法将如何表现。

在本例中,我们设置了一些分类选项,包括一个名为 labels 的第一个项目,它包含一个将用于界面中引用新分类的文本字符串数组。我们还指定了第二个元素,名为 show_ui,它控制分类元框在帖子编辑器中的显示以及访问分类编辑器在管理菜单中的链接的存在。接下来是一个名为 show_tagcloud 的选项,我们将其设置为 false 以避免显示所有分类值的标签云。最后,选项数组中的最后一个项目名为 hierarchical。当设置为 true 时,分类项将能够有父子关系,并且可以在帖子编辑器中以复选框列表的形式访问。如果设置为 false,所有分类都将组织成平面列表,并且可以使用类似于帖子编辑器和页面编辑器中标签窗口的界面进行选择。

register_taxonomy 函数有更多可用的选项,如访问 WordPress Codex 网站(codex.wordpress.org/Function_Reference/register_taxonomy)所示,但这里列出的都是定义基本分类法所必需的。

参见

  • 创建自定义帖子类型 的食谱

添加自定义字段到分类

除了指定分类的名称外,将附加额外信息到在 WordPress 插件中创建的自定义分类可能很有用。例如,我们可能想要为在显示时使用的分类分配自定义颜色,或者我们可能想要识别只有付费会员才能访问的内容分类。

本食谱展示了如何在分类编辑器中显示额外字段,以及如何将额外数据存储在网站的数据库中。

准备工作

你应该已经遵循了 添加自定义分类以用于自定义帖子类型 的食谱,以便为本食谱提供一个起点,并且生成的插件仍然在你的开发网站上处于激活状态。或者,你可以从下载的代码包中获取生成的代码(Chapter 4/ch4-book-reviews/ch4-book-reviews-v5.php),并将文件重命名为 ch4-book-reviews.php

如何操作...

  1. 导航到你的开发安装的 WordPress 插件目录中的 ch4-book-reviews 文件夹。

  2. 在代码编辑器中打开 ch4-book-reviews.php 文件。

  3. 在现有函数之后添加以下代码行,以将函数分配给两个将在用户创建或编辑分类项时被调用的动作钩子:

add_action( 'book_reviews_book_type_edit_form_fields', 
            'ch4_br_book_type_new_fields', 10, 2 );
add_action( 'book_reviews_book_type_add_form_fields',
            'ch4_br_book_type_new_fields', 10, 2 );
  1. 将以下代码段添加到提供 ch4_br_book_type_new_fields 函数实现的区域:
function ch4_br_book_type_new_fields( $tag ) {
    $mode = 'new';

    if ( is_object( $tag ) ) {
        $mode = 'edit';
        $book_cat_color = get_term_meta( $tag->term_id, 
                                         'book_type_color',
                                         true );
    }
    $book_cat_color = empty( $book_cat_color ) ? 
                          '#' : $book_cat_color;

    if ( 'edit' == $mode ) {
        echo '<tr class="form-field">';
        echo '<th scope="row" valign="top">';
    } elseif ( 'new' == $mode ) {
        echo '<div class="form-field">';
    } ?>

    <label for="tag-category-url">Color</label>
    <?php if ( 'edit' == $mode ) {
        echo '</th><td>';
    } ?>

    <input type="text" id="book_type_color" 
           name="book_type_color"
           value="<?php echo $book_cat_color; ?>" />
    <p class="description">Color associated with book type 
                           (e.g. #199C27 or #CCC)</p>

    <?php if ( 'edit' == $mode ) {
        echo '</td></tr>'; 
    } elseif ( 'new' == $mode ) {
        echo '</div>';
    }
}
  1. 在文件末尾添加以下代码行,以分配一个在用户创建或更新分类项时将被调用的函数:
add_action( 'edited_book_reviews_book_type',
            'ch4_br_save_book_type_new_fields', 10, 2 );
add_action( 'created_book_reviews_book_type',
            'ch4_br_save_book_type_new_fields', 10, 2 );
  1. 将以下代码段添加到提供 ch4_br_save_book_type_new_fields 函数实现的区域:
function ch4_br_save_book_type_new_fields( $term_id, $tt_id ) {
    if ( !$term_id ) {
        return;
    }

    if ( isset( $_POST['book_type_color'] ) 
             && ( '#' == $_POST['book_type_color'] 
         || preg_match( '/#([a-f0-9]{3}){1,2}\b/i', 
                        $_POST['book_type_color'] ) ) ) {
        $returnvalue = update_term_meta( $term_id, 
                           'book_type_color', 
                           $_POST['book_type_color'] );
    }
}
  1. 保存并关闭插件文件。

  2. 编辑先前的食谱中创建的其中一个书籍类型条目以查看新添加的颜色字段。输入一个颜色代码并更新条目以查看保存的数据:

图片

它是如何工作的...

此食谱使用许多变量名动作钩子来注册在分类编辑器中创建或修改书籍类型时要调用的函数。前两个对 add_action 的调用分别指的是 <taxonomy>_edit_forms_fields<taxonomy>_add_form_fields 钩子。虽然您可能期望看到与每个动作钩子相关联的两个不同的函数,但实际上我们在两种情况下都注册了相同的函数,因为渲染额外字段在这两种情况下是相似的。话虽如此,我们注册的函数的一部分会检查它是否接收一个有效的对象作为参数,以便知道它应该如何渲染新字段,以便如果用户正在创建新类别或编辑现有类别,它能够适合页面。

我们使用与 edited_<taxonomy>created_<taxonomy> 动作钩子类似的技术,这些钩子分别在您首次保存新的分类或更新现有分类时被调用。在这种情况下,代码在动作方面没有显著差异,因为我们只需要验证并保存新字段的传入值。

参见

  • 为自定义文章类型添加自定义分类 的食谱

从自定义文章类型编辑器中隐藏分类编辑器

正如我们在先前的食谱中看到的,当我们将新的分类与书籍评论自定义文章类型相关联时,show_ui 选项控制分类分配元框和到分类编辑器的管理员菜单链接的可见性。在某些情况下,我们希望用户能够访问完整的分类编辑器,但在自定义文章类型编辑器中创建新条目时,只让编辑器从受控的下拉列表中选择。

此食谱展示了如何隐藏分类界面从文章编辑器中,以及如何更新在先前的食谱中创建的自定义文章类型元框以分配类型给新的书籍评论并保存此信息到网站的数据库中。

准备工作

您应该已经遵循了 为分类添加自定义字段 的食谱,以便为此食谱提供一个起点,并且生成的插件仍然在您的开发站点中处于活动状态。或者,您可以从下载的代码包中获取生成的代码(Chapter 4/ch4-book-reviews/ch4-book-reviews-v6.php)并将文件重命名为 ch4-book-reviews.php

如何操作...

  1. 导航到您的开发安装的 WordPress 插件目录中的 ch4-book-reviews 文件夹。

  2. 打开代码编辑器中的 ch4-book-reviews.php 文件。

  3. ch4_br_create_book_post_type 函数中找到对 register_taxonomy 函数的调用,并添加一个名为 meta_box_cb 的新成员到配置数组中,其值设置为 false,如下所示(加粗):

   'show_tagcloud' => false, 
   'meta_box_cb' => false,
   'hierarchical' => true
  1. 保存插件并编辑一个书评,以查看书评类型分类框不再显示。

  2. 在代码中找到ch4_br_display_review_details_meta_box函数,并在现有的表格渲染代码内添加以下代码,以添加一个包含书籍类型下拉选择框的新行:

<tr> 
    <td>Book Type</td> 
    <td> 
    <?php 
        // Retrieve array of types assigned to post 
        $assigned_types = wp_get_post_terms( $book_review->ID, 
                              'book_reviews_book_type' ); 

        // Retrieve array of all book types in system 
        $book_types = get_terms( 'book_reviews_book_type', 
                                 array( 'orderby' => 'name',
                                        'hide_empty' => 0) ); 

        if ( $book_types ) { 
            echo '<select name="book_review_book_type"'; 
            echo ' style="width: 400px">'; 

            foreach ( $book_types as $book_type ) {                 
                echo '<option value="' . $book_type->term_id;
                echo '" '; 
                if ( !empty( $assigned_types ) ) {            
                    selected( $assigned_types[0]->term_id, 
                              $book_type->term_id );
                }
                echo '>' . esc_html( $book_type->name ); 
                echo '</option>'; 
            }         

            echo '</select>'; 
        } ?> 
    </td> 
</tr> 
  1. 找到ch4_br_add_book_review_fields函数,并在 if 语句内添加以下代码段,检查帖子类型是否为书评,以便在提交帖子时将选定的书籍类型保存到网站的数据库中:
if ( isset( $_POST['book_review_book_type'] ) 
     && !empty( $_POST['book_review_book_type'] ) ) { 
    wp_set_post_terms( $book_review->ID, 
                       intval( $_POST['book_review_book_type'] ), 
                       'book_reviews_book_type' ); 
} 
  1. 保存并关闭插件文件。

  2. 打开一个之前创建的书评,以查看包含一个新下拉列表以指定书籍类型的更新后的书评详细信息元框:

图片

它是如何工作的...

这个食谱使用register_taxonomy函数的许多参数之一来移除书评创建者和编辑创建新书籍类型的能力,并创建一个下拉列表,以便能够将单个书籍类型分配给书评。

在这个过程中,食谱使用了与存储和检索与帖子相关的分类条目相关的三个函数。第一个,wp_get_post_terms,根据帖子的 ID 和分类名称检索与帖子相关的一组术语。第二个,wp_set_post_terms,根据帖子的 ID 和分类名称分配一个术语给帖子。最后,get_terms检索分类中所有术语的数组,根据查询字符串按第二个参数中的顺序排列。

参见

  • 为自定义帖子类型添加自定义分类 食谱

在自定义帖子列表页面显示额外的列

在定制帖子编辑器以给内容创作者一个定制的环境来创建和编辑自定义帖子类型条目之后,这个食谱将注意力转向书评管理页面,其中列出了此类型的所有条目。默认情况下,自定义帖子类型列表相当简单,仅显示每个项目的标题、发布日期和评论数量。为了使在此管理页面中识别、排序和查找数据更容易,WordPress 提供了一系列定制功能,首先是更改显示列的能力。

这个食谱展示了如何在帖子管理页面中添加和删除列,以及在新列中进行排序。

准备工作

你应该已经遵循了从自定义帖子类型编辑器中隐藏分类编辑器食谱,以便为这个食谱提供一个起点,并且生成的插件应该仍然在你的开发站点中处于活动状态。或者,你可以从下载的代码包中获取生成的代码(Chapter 4/ch4-book-reviews/ch4-book-reviews-v7.php),并将文件重命名为ch4-book-reviews.php

如何去做...

  1. 导航到你的开发安装的 WordPress 插件目录中的ch4-book-reviews文件夹。

  2. 在代码编辑器中打开ch4-book-reviews.php文件。

  3. 在现有函数之后添加以下代码行,以注册一个在准备书籍评论列表页面时将被调用的函数:

add_filter( 'manage_edit-book_reviews_columns',
            'ch4_br_add_columns' ); 
  1. 添加以下代码段以提供ch4_br_add_columns函数的实现:
function ch4_br_add_columns( $columns ) { 
    $columns['book_reviews_author'] = 'Author'; 
    $columns['book_reviews_rating'] = 'Rating'; 
    $columns['book_reviews_type'] = 'Type'; 

    unset( $columns['comments'] ); 

    return $columns; 
} 
  1. 添加以下代码行以分配一个函数,当检索帖子列表中每行的列数据时将被调用:
add_action( 'manage_posts_custom_column', 
            'ch4_br_populate_columns' ); 
  1. 插入以下代码段以提供ch4_br_populate_columns函数的实现:
function ch4_br_populate_columns( $column ) { 
    if ( 'book_reviews_author' == $column ) { 
        $book_author = esc_html( get_post_meta( get_the_ID(), 
                                                'book_author', 
                                                true ) ); 
        echo $book_author; 
    } elseif ( 'book_reviews_rating' == $column ) { 
        $book_rating = get_post_meta( get_the_ID(), 'book_rating', 
                                      true ); 
        echo $book_rating . ' stars'; 
    } elseif ( 'book_reviews_type' == $column ) {
        $book_types = wp_get_post_terms( get_the_ID(), 
                          'book_reviews_book_type' );                 
        if ( $book_types ) {
            $book_cat_color = get_term_meta( 
                                  $book_types[0]->term_id,
                                  'book_type_color', true );

            if ( '#' != $book_cat_color ) {
                echo '<span style="background-color: ';
                echo $book_cat_color . '; ';
                echo 'color: #fff; padding: 6px;">';
                echo $book_types[0]->name . '</span>';
            } else {
                echo $book_types[0]->name;
            }
        } else {
            echo 'None Assigned'; 
        }
    } 
} 
  1. 保存插件文件并导航到书籍评论列表页面,以查看列列表已被更改,并且存储在帖子自定义字段中的数据现在显示在列表中的每个条目上。

  2. 在代码编辑器中,添加以下代码到插件文件的末尾,以注册一个在 WordPress 识别书籍评论自定义帖子类型可排序列时将被调用的函数:

add_filter( 'manage_edit-book_reviews_sortable_columns', 
            'ch4_br_author_column_sortable' ); 
  1. 添加以下代码以提供ch4_br_author_column_sortable函数的实现:
function ch4_br_author_column_sortable( $columns ) { 
    $columns['book_reviews_author'] = 'book_reviews_author'; 
    $columns['book_reviews_rating'] = 'book_reviews_rating'; 

    return $columns; 
} 
  1. 添加以下代码块以注册一个函数,当请求显示帖子列表的数据时将被调用:
add_filter( 'request', 'ch4_br_column_ordering' ); 
  1. 插入以下代码段以实现ch4_br_column_ordering函数:
function ch4_br_column_ordering( $vars ) { 
    if ( !is_admin() ) {
        return $vars;
    }

    if ( isset( $vars['orderby'] ) &&  
         'book_reviews_author' == $vars['orderby'] ) { 
        $vars = array_merge( $vars, array( 
            'meta_key' => 'book_author', 
            'orderby' => 'meta_value' ) ); 
    } elseif ( isset( $vars['orderby'] ) &&  
               'book_reviews_rating' == $vars['orderby'] ) { 
        $vars = array_merge( $vars, array( 
                                 'meta_key' => 'book_rating', 
                                 'orderby' => 'meta_value_num' ) ); 
    } 
    return $vars; 
} 
  1. 保存并关闭插件文件。

  2. 刷新书籍评论列表以查看作者和评分列标题是可点击的链接,可以点击以对这些列进行排序:

图片

它是如何工作的...

定制帖子列表页面需要精心混合动作和过滤器钩子以实现最终目标。我们注册的第一个函数与变量过滤器名称manage_edit-<post_type>_columns相关联,其中<post_type>被替换为内部帖子类型名称。当注册的函数被调用时,它接收将作为参数显示在列出书籍评论条目时的默认列列表。使用这些数据,它继续添加authorratingtype三个列,并从数组中删除comments列。一旦完成,它返回修改后的数组。

菜谱的第二部分注册了负责填充新列的函数。由于此函数在渲染任何自定义帖子类型列时被调用,代码在将请求的数据回显到浏览器之前检查当前请求的是哪个列。该函数调用get_the_ID()以获取当前显示行的索引,并使用get_post_metawp_get_post_terms找到其关联数据。

在本配方的这个阶段,新列在书籍评论管理页面中可见,并且每个列都显示数据。本配方剩余部分的目的是将author(作者)和rating(评分)列设置为可排序。这是通过首先注册一个名为manage_edit-<post_type>_sortable_columns的变量函数来完成的,其中<post_type>被帖子类型名称替换。当函数执行时,它向将要排序的列数组中添加两个项目。这处理了使列标题链接(可以点击进行排序)与适当的 URL 相关联。

最后注册的函数与请求过滤器相关联,负责根据查询 URL 中传入的变量向查询数组添加元素。

最终结果允许管理员根据这两个可排序的列轻松重新排列书籍评论,同时还可以查看每个条目的类型信息。

参见

  • 为自定义帖子类型添加自定义类别 的配方

将自定义类别筛选器添加到自定义帖子列表页面

对于自定义帖子列表的第二个自定义方法是创建一个下拉框,允许管理员一次只显示属于单个类别的项目。这可以显著减少显示的条目数量,从而快速找到所需的条目。

本配方展示了如何将基于书籍评论类型的筛选机制添加到列表页面。

准备工作

您应该已经遵循了 在自定义帖子列表页面显示额外列 的配方,以便为本配方提供一个起点,并且生成的插件仍然在您的开发站点中激活。或者,您可以从下载的代码包中获取生成的代码(Chapter 4/ch4-book-reviews/ch4-book-reviews-v8.php),并将文件重命名为ch4-book-reviews.php

如何操作...

  1. 导航到您开发安装的 WordPress 插件目录中的ch4-book-reviews文件夹。

  2. 在代码编辑器中打开ch4-book-reviews.php文件。

  3. 在现有函数之后添加以下代码行,以注册一个在 WordPress 准备帖子列表的筛选下拉框时调用的函数:

add_action( 'restrict_manage_posts',
            'ch4_br_book_type_filter_list' ); 
  1. 添加以下代码段以提供ch4_br_book_type_filter_list函数的实现:
function ch4_br_book_type_filter_list() { 
    $screen = get_current_screen(); 
    global $wp_query; 
    if ( 'book_reviews' == $screen->post_type ) { 
        wp_dropdown_categories( array( 
            'show_option_all' =>  'Show All Book Types', 
            'taxonomy'        =>  'book_reviews_book_type', 
            'name'            =>  'book_reviews_book_type', 
            'orderby'         =>  'name', 
            'selected'        =>  
            ( isset( $wp_query->query['book_reviews_book_type'] )         
              ? $wp_query->query['book_reviews_book_type'] : '' ), 
            'hierarchical'    =>  false, 
            'depth'           =>  3, 
            'show_count'      =>  false, 
            'hide_empty'      =>  true, 
            ) ); 
    } 
} 
  1. 在准备帖子显示查询时调用函数的注册中插入以下代码行:
add_filter( 'parse_query', 'ch4_br_perform_book_type_filtering' );
  1. 使用以下代码段实现ch4_br_perform_book_type_filtering函数:
function ch4_br_perform_book_type_filtering( $query ) {
    $qv = &$query->query_vars; 

    if ( isset( $qv['book_reviews_book_type'] ) &&
         !empty( $qv['book_reviews_book_type'] ) && 
         is_numeric( $qv['book_reviews_book_type'] ) ) { 
        $term = get_term_by( 'id', 
                             $qv['book_reviews_book_type'], 
                             'book_reviews_book_type' );     
        $qv['book_reviews_book_type'] = $term->slug; 
    } 
} 
  1. 保存并关闭插件文件。

  2. 访问书籍评论列表,查看新的下拉框以限制显示的书籍类型:

图片

工作原理...

本食谱首先注册一个动作回调,该回调将在 WordPress 渲染每个帖子类型列表可用的各种过滤器控件时执行。当函数被调用时,它检索一个全局变量以了解当前显示的帖子类型,并确定是否应该显示书籍类型过滤器列表。它还访问全局帖子查询变量,查看是否已经放置了书籍类型过滤器,并在有可用的条目时设置正确的下拉列表条目为选中状态。

回调随后继续使用wp_dropdown_categories函数来显示为书籍类型注册的所有分类法项的列表。这个实用函数期望接收一个参数数组,该数组确定要显示哪个分类法列表,下拉列表字段的名称,以及显示所有类型的标签。此数组还应包含一些参数,以确定项目应显示的顺序,指定要设置为选中的项目,指示显示分层分类法的最大深度,并确定是否显示项目计数和空项目。

一旦新的书籍类型选择列表就位,选择一个条目并点击过滤器按钮将触发网页刷新,并导致执行后放置的第二注册回调。过滤器函数接收当前的 WordPress 帖子查询对象,首先获取存储在查询对象内部的查询变量的指针。有了这个,它继续验证书籍类型是否是查询变量的一部分,并且它是数字的。如果结果是正的,它将数字值替换为所选书籍类型的文本名称,以便查询可以进行。

一旦执行所有这些代码,用户就可以快速过滤在书评管理页面上应显示哪些书籍类型。他们仍然可以使用之前食谱中实现的列排序机制。

参见

  • 为自定义帖子类型添加自定义分类 的食谱

为自定义分类添加快速编辑字段

WordPress 的一个伟大功能是网站编辑员可以通过点击与任何显示的项目关联的快速编辑链接来快速更改管理部分中的任何帖子。虽然我们的自定义帖子类型分类法出现在快速编辑部分,但它不是一个选择列表,就像我们在书评编辑器中有的那样。此外,作者和评分字段在快速编辑部分中根本不显示。

本食谱展示了如何在快速编辑书评时添加自定义字段。当你执行以下步骤时,你会发现我们放置的一些代码不如之前食谱中的代码整洁,因为 WordPress 快速编辑自定义基础设施不如平台的其他区域结构良好。

准备工作

您应该已经遵循了为自定义分类添加过滤器到自定义帖子列表页面的配方,以获得此配方的起点,并且生成的插件仍然在您的开发站点中处于活动状态。或者,您可以从下载的代码包中获取生成的代码(Chapter 4/ch4-book-reviews/ch4-book-reviews-v9.php)并将文件重命名为ch4-book-reviews.php

如何做到这一点...

  1. 导航到您开发安装的 WordPress 插件目录中的ch4-book-reviews文件夹。

  2. 在代码编辑器中打开ch4-book-reviews.php文件。

  3. ch4_br_create_book_post_type函数中找到对register_taxonomy函数的调用,并在配置数组中添加一个名为show_in_quick_edit的新成员,其值设置为false,并用粗体突出显示:

'show_tagcloud' => false, 
'meta_box_cb' => false,
'show_in_quick_edit' => false,
'hierarchical' => true,
  1. 在现有函数之后添加以下代码行以注册一个在 WordPress 准备渲染快速编辑部分内容时要调用的函数:
add_action( 'quick_edit_custom_box',
            'ch4_br_display_custom_quickedit_link', 10, 2 );
  1. 将以下代码段添加到为ch4_br_display_custom_quickedit_link函数提供实现的代码:
function ch4_br_display_custom_quickedit_link( $column_name, 
                                               $post_type ) {
    if ( 'book_reviews' == $post_type ) {
        switch ( $column_name ) {
            case 'book_reviews_author': ?>
                <fieldset class="inline-edit-col-right">
                <div class="inline-edit-col">
                   <label><span class="title">Author</span></label>
                   <input type="text"       
                          name='book_reviews_author_input'
                          id='book_reviews_author_input' value="">
                </div>
                <?php break;
            case 'book_reviews_rating': ?>
                <div class="inline-edit-col">
                    <label>
                        <span class="title">Rating</span>
                    </label>
                    <select name='book_reviews_rating_input'
                            id='book_reviews_rating_input'>
                    <?php // Generate all items of drop-down list 
                    for ( $rating = 5; $rating >= 1; $rating -- ) {
                    ?>  <option value="<?php echo $rating; ?>">
                        <?php echo $rating; ?> stars 
                    <?php } ?> 
                    </select>
                </div>
                <?php break;
            case 'book_reviews_type': ?>
                <div class="inline-edit-col">
                    <label><span class="title">Type</span></label>
                    <?php
                    $terms = get_terms( array( 'taxonomy' => 
                                        'book_reviews_book_type',
                                        'hide_empty' => false ) );
                    ?>
                    <select name='book_reviews_type_input'
                            id='book_reviews_type_input'>
                    <?php foreach ($terms as $index => $term) {
                        echo '<option ';
                        echo 'class="book_reviews_type-option"';
                        echo 'value="' . $term->term_id . '"';
                        selected( 0, $index );
                        echo '>' . $term->name. '</option>';
                    } ?>
                    </select>
                </div>
            <?php break;
        } 
    } 
}
  1. 添加以下代码行以注册一个在 WordPress 渲染管理页面页脚时要调用的函数:
add_action( 'admin_footer', 'ch4_br_quick_edit_js' );
  1. 使用以下代码片段实现ch4_br_quick_edit_js函数:
function ch4_br_quick_edit_js() {
    global $current_screen;
    if ( ( 'edit-book_reviews' !== $current_screen->id ) ||
         ( 'book_reviews' !== $current_screen->post_type ) ) {
        return;
    } ?>

    <script type="text/javascript">
    function set_inline_book_reviews( reviewArray ) {
        // revert Quick Edit menu so that it refreshes properly
        inlineEditPost.revert();
        var inputBookAuthor = 
            document.getElementById('book_reviews_author_input');
        inputBookAuthor.value = reviewArray[0];

        var inputRating =
            document.getElementById('book_reviews_rating_input');
        for (i = 0; i < inputRating.options.length; i++) {
            if ( inputRating.options[i].value == reviewArray[1] ) {
                inputRating.options[i].setAttribute( 'selected',
                                                     'selected' );
            } else {
                inputRating.options[i].removeAttribute( 
                    'selected' );
            }
        } 

        var inputBookType =
            document.getElementById('book_reviews_type_input');
        for (i = 0; i < inputBookType.options.length; i++) {
            if ( inputBookType.options[i].value == 
                     reviewArray[2] ) {
                inputBookType.options[i].setAttribute( 'selected',
                    'selected' );
            } else {
                inputBookType.options[i].removeAttribute( 
                    'selected' );
            }
        } 
    }
</script>
<?php }
  1. 将以下代码添加到注册一个函数以替换为每个帖子在书评页面生成的原始快速编辑代码:
add_filter( 'post_row_actions', 'ch4_br_quick_edit_link', 10, 2 );
  1. 添加以下代码块以提供ch4_br_quick_edit_link函数的实现:
function ch4_br_quick_edit_link( $act, $post ) {
    global $current_screen;
    $post_id = '';

    if ( ( isset( $current_screen ) && 
           $current_screen->id != 'edit-book_reviews' &&
           $current_screen->post_type != 'book_reviews' ) 
         || ( isset( $_POST['screen'] ) &&
              $_POST['screen'] != 'edit-book_reviews' ) ) {
        return $act;
    }

    if ( !empty( $post->ID ) ) {
        $post_id = $post->ID;
    } elseif ( isset( $_POST['post_ID'] ) ) {
        $post_id = intval( $_POST['post_ID'] );
    }

    if ( !empty( $post_id ) ) {
        $book_author = esc_html( get_post_meta( $post_id, 
                                     'book_author', true ) ); 
        $book_rating = esc_html( get_post_meta( $post_id, 
                                     'book_rating', true ) );
        $book_reviews_types = wp_get_post_terms( $post_id, 
                                     'book_reviews_book_type',
                                     array( 'fields' => 'all' ) );
        if ( empty( $book_reviews_types ) ) {
            $book_reviews_types[0] = 
                (object) array( 'term_id' => 0 );
        }

        $idx = 'inline hide-if-no-js';
        $act[$idx] = '<a href="#" class="editinline"  ';
        $act[$idx] .= " onclick=\"var reviewArray = new Array('";
        $act[$idx] .= "{$book_author}', '{$book_rating}', ";
        $act[$idx] .= "'{$book_reviews_types[0]->term_id}');";
        $act[$idx] .= "set_inline_book_reviews( reviewArray )\">";
        $act[$idx] .= __( 'Quick&nbsp;Edit' );
        $act[$idx] .= '</a>';
    }
    return $act;
}
  1. 添加以下函数调用以注册一个在从快速编辑部分更新帖子数据时将执行的函数:
add_action( 'save_post', 'ch4_br_save_quick_edit_data', 10, 2 );
  1. 使用以下代码块为ch4_br_save_quick_edit_data函数提供实现:
function ch4_br_save_quick_edit_data( $ID = false, 
                                      $post = false ) {
    // Do not save if auto-saving, not book reviews, no permissions
    if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) ||
         ( isset( $_POST['post_type'] ) 
           && 'book_reviews' != $_POST['post_type'] ) ||
         !current_user_can( 'edit_page', $ID ) ) {
        return $ID;
    }

    $post = get_post( $ID );
    if ( !empty( $post ) && 'revision' != $post->post_type ) {
        if ( isset( $_POST['book_reviews_author_input'] ) ) {
            update_post_meta( $ID, 'book_author', 
                sanitize_text_field( 
                    $_POST['book_reviews_author_input'] ) ); 
        }

        if ( isset( $_POST['book_reviews_rating_input'] ) ) {
            update_post_meta( $ID, 'book_rating', 
                intval( $_POST['book_reviews_rating_input'] ) ); 
        }

        if ( isset( $_POST['book_reviews_type_input'] ) ) {
            $term = term_exists( 
                intval( $_POST['book_reviews_type_input'] ),
                        'book_reviews_book_type' );
            if ( !empty( $term ) ) {
                wp_set_object_terms( $ID, 
                    intval( $_POST['book_reviews_type_input'] ), 
                            'book_reviews_book_type' );
            }
        }
    } 
}
  1. 保存并关闭插件文件。

  2. 访问书评列表页面并点击快速编辑以查看新添加的作者、评分和类型字段。

图片

  1. 更改这些字段中的值,并使用更新按钮保存它们。您将看到相应的值在书评列表中更新。

它是如何工作的...

虽然快速编辑框是一个 WordPress 用户非常欣赏的强大工具,但在当前版本的 WordPress 中对其进行自定义有点棘手。虽然我们可以轻松地向每个部分中的每个单独的项目添加数据,例如帖子编辑器,但为了我们新的自定义帖子类型,快速编辑部分需要更多的工作来添加自定义字段。问题的根源在于,实际上只有一个快速编辑部分的实例被渲染为编辑页面的部分。该部分最初是隐藏的,然后在用户点击快速编辑链接时出现,并移动到正确的位置。考虑到这一点,我们无法在快速编辑框中为自定义字段分配适当的值,因此需要将隐藏信息存储在每个项目被列出时,以便能够根据正在编辑的项目适当更新每个字段。

我们食谱代码的第一部分从渲染快速编辑部分中的文本输入字段和两个下拉列表开始。你会注意到我们没有设置文本字段的值,也没有设置选择列表中的选项。这个回调的另一个有趣之处在于,它发送给我们与我们在在自定义帖子列表页面中显示额外列食谱中使用的相同列列表。这意味着如果我们想添加除了我们已添加到帖子表中的字段之外的其他字段,我们仍然需要将这些字段名称放入列列表中,然后使用一些技巧来隐藏它们从表中。

一旦这些额外字段就位,我们将在书评编辑器的管理页面页脚中添加代码,创建一个当需要更新快速编辑部分中的新自定义字段时我们将调用的 JavaScript 函数。该函数接收一个数据数组,然后使用document.getElementById函数在页面上定位自定义字段,并根据传入的数据数组更新它们的值。

我们添加的下一块代码创建了一个新的快速编辑链接,用于替换每个书评项目的原始链接。新的链接不仅允许用户显示快速编辑部分,还将在onclick JavaScript 代码中嵌入每个项目的值,以及调用我们添加到页脚的函数,以便当用户决定快速编辑书评时,为每个字段分配一组新的值。

最后,当用户点击更新按钮时,我们存储来自自定义字段的数据。有趣的是,我们通过为save_post动作注册第二个回调来实现这一点。这意味着当帖子被保存时,我们的两个函数都会被调用。然而,这个第二个保存函数在实际上保存值之前会检查多个条件是否为真,并且它保存的字段名称与我们为帖子编辑器放置的保存函数不同。

使用插件过滤器更新页面标题以包含自定义帖子数据

为了支持我们的书评自定义帖子类型,可以添加到标题栏中的自定义信息作为最后的定制触摸。例如,我们可以在书名旁边添加作者的名字。

这个配方展示了如何使用document_title_parts过滤器来更改书评的帖子标题。

准备工作

你应该已经遵循了为自定义分类添加快速编辑字段的配方,以便为这个配方提供一个起点,并且生成的插件应该仍然在你的开发站点中处于活动状态。或者,你可以从下载的代码包中获取生成的代码(Chapter 4/ch4-book-reviews/ch4-book-reviews-v10.php),并将文件重命名为ch4-book-reviews.php

如何操作...

  1. 导航到你的开发安装的 WordPress 插件目录中的ch4-book-reviews文件夹。

  2. 在代码编辑器中打开ch4-book-reviews.php文件。

  3. 在现有函数之后添加以下代码行,以注册一个在 WordPress 准备在浏览器标题栏中显示的文本时要调用的函数:

add_filter( 'document_title_parts', 
            'ch4_br_format_book_review_title' ); 
  1. 添加以下代码段以提供ch4_br_format_book_review_title函数的实现:
function ch4_br_format_book_review_title( $the_title ) { 
    if ( 'book_reviews' == get_post_type() && is_single() ) { 
        $book_author = esc_html( get_post_meta( get_the_ID(),  
                                   'book_author', true ) );
        if ( !empty( $book_author ) ) {
            $the_title['title'] .= ' by ' . $book_author;
        }
    } 

    return $the_title; 
} 
  1. 保存并关闭插件文件。

  2. 访问一个书评页面。你会看到书的作者现在显示在标题中的名字之后:

图片

工作原理...

document_title_parts 过滤器允许插件修改或完全替换页面标题内容。在这种情况下,我们与过滤器钩子关联的函数的代码接收 WordPress 打算显示为数组的标题。然后它继续检查正在准备显示的项目是否为书评,以及它是否是一个单独的项目。虽然第一个条件是显而易见需要检查的,但is_single验证是为了确保代码不会尝试将书作者添加到书评存档列表页面。

第五章:自定义帖子和页面编辑器

在本章中,您将通过以下食谱学习如何通过以下方法自定义核心帖子和页面编辑器:

  • 使用自定义元框捕获和显示信息

  • 通过过滤器函数显示自定义帖子数据

  • 在帖子编辑器中隐藏自定义字段部分

  • 扩展帖子编辑器以允许用户直接上传文件

简介

在最后几章中,您学习了如何创建自定义插件配置面板和如何设置自定义帖子类型。掌握这些知识后,我们现在将看到如何使用元框自定义帖子和页面编辑器。

元框是创建 WordPress 插件的一个非常有用的工具。它们最初用于将大型管理面板组织成可管理的部分,如第三章中所述,用户设置和管理页面,然后继续成为第四章中创建定制界面以编辑自定义帖子类型的关键元素,自定义帖子类型的威力

本章探讨了如何使用元框来增强默认帖子和页面编辑器的功能。虽然 WordPress 的帖子和页面在默认安装中已经提供了很多功能,但自定义数据输入字段在制作比使用自定义字段编辑器更流畅的用户体验方面发挥了重要作用。这些额外字段可以用来存储任何内容。例如,它们可以用来指定博客条目的替代语言链接或将弹出对话框分配给网站上特定的文章。

使用自定义元框捕获和显示信息

WordPress 的帖子和页面编辑器组织成一系列可折叠的带有标题的元框。虽然 WordPress 主要负责用所有适当元素填充这些容器,但插件开发者可以通过注册用户元框来插入自己的部分。

为了展示这一功能,本食谱展示了如何添加一个自定义元框,该元框将用于显示和捕获在撰写新帖子或页面条目时使用的源材料的名称和网页地址。

准备工作

您应该能够访问一个 WordPress 开发环境,无论是在您的本地计算机上还是在远程服务器上,您将能够加载您的新插件文件。

如何操作...

  1. 导航到您的开发安装的 WordPress 插件目录。

  2. 创建一个名为ch5-post-source-link的新目录。

  3. 导航到目录并创建一个名为ch5-post-source-link.php的文本文件。

  4. 在代码编辑器中打开新文件,并在插件文件顶部添加一个适当的标题,将插件命名为第五章 - 帖子源链接

  5. 添加以下代码行以注册一个函数,该函数将在 WordPress 准备所有管理区域元框列表时执行:

add_action( 'add_meta_boxes', 'ch5_psl_register_meta_box' );
  1. 添加以下代码段以提供ch5_psl_register_meta_box函数的实现:
function ch5_psl_register_meta_box() { 
    add_meta_box( 'ch5_psl_source_meta_box', 'Post/Page Source',
                  'ch5_psl_source_meta_box', 'post', 'normal'); 
    add_meta_box( 'ch5_psl_source_meta_box', 'Post/Page Source',
                  'ch5_psl_source_meta_box', 'page', 'normal'); 
} 
  1. 插入以下代码以提供ch5_psl_source_meta_box函数的实现,该函数负责渲染元框内容:
function ch5_psl_source_meta_box( $post ) {  
    // Retrieve current source name and address for post 
    $post_source_name =
        esc_html( get_post_meta( $post->ID, 'post_source_name',
                                 true ) ); 
    $post_source_address =
        esc_html( get_post_meta( $post->ID, 
                                 'post_source_address', 
                                 true ) ); 
    ?> 
    <!-- Display fields to enter source name and address --> 
    <table> 
        <tr> 
            <td style="width: 100px">Source Name</td> 
            <td> 
                <input type="text" size="40" 
                       name="post_source_name"
                       value="<?php echo $post_source_name; ?>" /> 
            </td> 
        </tr> 
        <tr> 
            <td>Source Address</td> 
            <td> 
                <input type="text" size="40" 
                       name="post_source_address"
                       value="<?php echo $post_source_address; ?>"
                /> 
            </td> 
        </tr> 
    </table> 
<?php } 
  1. 插入以下代码块以注册一个函数,该函数将在任何类型的帖子保存时被调用:
add_action( 'save_post', 'ch5_psl_save_source_data', 10, 2 ); 
  1. 添加以下代码段以提供ch5_psl_save_source_data函数的实现:
function ch5_psl_save_source_data( $post_id = false,  
                                   $post = false ) { 
    // Check post type for posts or pages 
    if ( 'post' == $post->post_type || 
         'page' == $post->post_type ) { 
        // Store data in post meta table if present in post data 
        if ( isset( $_POST['post_source_name'] ) ) {
            update_post_meta( $post_id, 'post_source_name', 
                sanitize_text_field(
                    $_POST['post_source_name'] ) ); 
        }

        if ( isset( $_POST['post_source_address'] ) ) {
            update_post_meta( $post_id, 'post_source_address', 
                esc_url( $_POST['post_source_address'] ) ); 
        }
    } 
} 
  1. 保存并关闭插件文件。

  2. 导航到插件管理页面并激活“第五章 - 帖子源链接”插件。

  3. 前往管理后台的“帖子”部分,点击其中一个条目以打开帖子编辑器,并查看新的“帖子/页面源”元框:

图片

它是如何工作的...

每当管理员或内容经理访问平台的后端时,WordPress 都会使用我们在前两章中看到的add_meta_box函数为所有内部编辑器(帖子、页面、链接等)创建多个元框。

在这个菜谱中,我们将使用两次add_meta_box函数来将新框关联到页面和帖子编辑器,由于我们希望在这两个地方都有相同的功能,因此两次调用都注册了相同的回调函数。由于 WordPress 将帖子存储在同一个数据库表中,它将自动确保两种类型的条目之间具有唯一的 ID。

我们之前看到的另一个函数是get_post_meta,它用于检索与帖子条目关联的自定义元数据。

元框本身的内容使用标准 HTML 显示。由于此框将是更大编辑器的一部分,因此无需担心在此框中声明表单。

一旦创建新的对话框部分,接下来的任务就是通过使用save_post动作来存储从附加字段提交的数据。当保存任何类型的帖子时,与该钩子相关联的函数会被调用。当执行时,相关的函数会从 WordPress 接收两个参数,包含正在保存的帖子的 ID 以及迄今为止已处理并准备保存的所有数据的副本。回调函数还可以访问从用户那里接收到的所有服务器帖子数据,并使用sanitize_text_fieldesc_url函数来确保没有接收到恶意内容。

如前一章所述,对于接收多个参数的操作和过滤器,设置add_action调用的第四个参数——即accepted_args——非常重要。如果没有指定,这些额外的参数将不可用于接收钩子函数。

假设元框只添加到帖子编辑器和页面编辑器中,ch5_psl_save_source_data函数中的代码首先检查帖子类型是否设置为帖子或页面。如果是这两种类型之一,它将继续检查源名称和地址字段是否存在帖子数据。如果找到,则对每个字段调用一次update_post_meta来将新信息存储在与其所属帖子关联的网站数据库中。调用update_post_meta函数实际上会更新帖子自定义字段数据(如果存在)或在新帖子或页面条目中创建它。

更多内容...

虽然这个配方专门为帖子编辑器和页面编辑器添加了一个新部分,但可能希望将新字段提供给所有帖子类型,包括自定义类型。

向所有帖子类型(包括自定义类型)添加新的元框

这个配方对注册元框调用了两次函数,用于注册帖子编辑器和页面编辑器。由于其他插件创建的自定义类型是未知的,因此这个概念不适合注册所有帖子类型的自定义部分。幸运的是,有一个简单的方法可以通过快速foreach循环获取所有帖子类型的数组,以便将新元框与所有帖子编辑器关联起来。

以下代码展示了如何重写ch5_psl_register_meta_box函数,以便将新框与所有帖子类型关联:

function ch5_psl_register_meta_box() { 
    $post_types = get_post_types( array(), 'objects' ); 
    foreach ( $post_types as $post_type ) { 
        add_meta_box( 'ch5_psl_post_source_meta_box', 
                      'Post/Page Source', 
                      'ch5_psl_source_meta_box', 
                      $post_type->name, 'normal' );  
    } 
} 

使用过滤器函数显示自定义帖子数据

一旦我们在帖子编辑器中捕获了额外的数据,下一步逻辑上将是添加代码,以便在访客查看帖子时显示它。在我们的源链接数据的情况下,最合理的地方是在每篇帖子内容之后显示此链接,这可以通过将函数分配给过滤器the_content轻松实现。

这个配方解释了如何创建一个过滤器函数,以显示与帖子或页面项关联的源数据,并以干净的链接形式显示。

准备工作

你应该已经遵循了使用自定义元框捕获和显示信息配方,以便为这个配方提供一个起点,并且生成的插件仍然在你的开发站点中处于活动状态。或者,你可以从 Packt Publishing 网站下载的代码包(www.packtpub.com/support)中获取生成的代码(Chapter 5/ch5-post-source-link/ch5-post-source-link-v1.php),并将文件重命名为ch5-post-source-link.php

如何操作...

  1. 导航到你的开发安装的 WordPress 插件目录中的ch5-post-source-link文件夹。

  2. 在代码编辑器中打开ch5-post-source-link.php文件。

  3. 添加以下代码行以注册一个在准备显示帖子内容时调用的过滤器函数:

add_filter( 'the_content', 'ch5_psl_display_source_link' );
  1. 将以下代码段添加到为ch5_psl_display_source_link函数提供实现的区域:
function ch5_psl_display_source_link ( $content ) { 
    $post_id = get_the_ID();

    if ( !empty( $post_id ) ) {
        if ( 'post' == get_post_type( $post_id ) ||
             'page' == get_post_type( $post_id ) ) {

            // Retrieve current source name and address for post 
            $post_source_name =
                get_post_meta( $post_id, 'post_source_name', 
                               true ); 
            $post_source_address =
                get_post_meta( $post_id, 'post_source_address', 
                               true ); 

            // Output information to browser 
            if ( !empty( $post_source_name ) && 
                 !empty( $post_source_address ) ) { 
                $content .= '<div class="source_link">Source: '; 
                $content .= '<a href="';
                $content .= esc_url( $post_source_address ); 
                $content .= '">' . esc_html( $post_source_name );
                $content .= '</a></div>';
            } 
        }
    }
    return $content;
} 
  1. 保存并关闭插件文件。

  2. 将源数据添加到您网站上的一个帖子中,并查看它以查看页面上显示的新源链接。

图片

它是如何工作的...

类似于在第二章,“插件框架基础”中,在“使用插件过滤器在每个项目内容后添加文本”配方中执行的操作,这个配方使用the_content过滤器钩子注册了一个回调,这允许我们在每个已填写源名称和源地址字段的帖子或页面后添加一个额外的链接。我们在回调中使用get_post_type函数首先检查显示的项目是否属于这两个类别之一。然后,我们使用get_post_meta检索信息,如果两个字段都包含信息,则显示源链接。

我们还使用esc_urlesc_html函数作为预防措施,确保存储在这两个帖子元字段中的数据是干净的。

参见

  • 使用自定义元框捕获和显示信息配方

在帖子编辑器中隐藏自定义字段部分

在完全控制创建自定义帖子类型编辑控件和组合插件配置页面时显示哪些元框之后,当涉及到更改基本帖子编辑器时,事情就有些不同了。更具体地说,不是选择要显示哪些元框,而是需要删除 WordPress 创建的编辑器部分,以定制用户体验。

这个配方展示了如何从帖子编辑器中删除自定义字段元框:

图片

准备工作

您应该能够访问一个 WordPress 开发环境,无论是在您的本地计算机上还是在远程服务器上,您将能够加载您的新插件文件。

如何操作...

  1. 导航到您开发安装的 WordPress 插件目录。

  2. 创建一个名为ch5-hide-custom-fields的新目录。

  3. 导航到这个目录并创建一个名为ch5-hide-custom-fields.php的新文本文件。

  4. 在代码编辑器中打开新文件,并在插件文件顶部添加一个适当的标题,将插件命名为第五章 - 隐藏自定义字段

  5. 将以下代码行添加到注册一个函数中,该函数将在 WordPress 准备所有管理区域元框列表时执行:

add_action( 'add_meta_boxes', 
            'ch5_hcf_remove_custom_fields_metabox' ); 
  1. 将以下代码段添加到为ch5_hcf_remove_custom_fields_metabox函数提供一个实现:
function ch5_hcf_remove_custom_fields_metabox() { 
    remove_meta_box( 'postcustom', 'post', 'normal' ); 
    remove_meta_box( 'postcustom', 'page', 'normal' ); 
} 
  1. 保存并关闭插件文件。

  2. 导航到插件管理页面并激活第五章 - 隐藏自定义字段插件。

  3. 前往管理后台的帖子部分,点击其中一个条目以打开帖子编辑器。您将看到编辑器中不再可见自定义字段部分,也不会在屏幕选项配置标签中显示:

图片

它是如何工作的...

本简短食谱仅包含几行代码,这些代码注册了一个在 WordPress 准备所有管理部分的元框时要调用的函数,然后是实现此函数。该函数本身对remove_meta_box函数进行了两次调用,以从帖子编辑器和页面编辑器中删除自定义字段部分。此函数需要三个参数:

remove_meta_box( $id, $page, $context );  

第一个参数是在首次创建时使用的元框标识符。虽然您可能不知道给定元框的创建代码位于 WordPress 源代码的何处,但快速查看浏览器页面源中的框的div id可以揭示其名称。在这种情况下,idpostcustom。其他两个参数表示要从哪个编辑器中删除元框的名称以及元框的上下文(normaladvancedside)。

一旦插件被激活,指定的框会立即从界面中消失。

扩展帖子编辑器以允许用户直接上传文件

WordPress 提供了一个非常完整的媒体上传对话框。然而,一些项目可能需要用户能够直接从帖子编辑器中附加文件。本食谱展示了如何修改帖子编辑器表单以能够将文件附加到文章中,以及如何在文件上传后存储这些文件。虽然可以使用此技术附加任何类型的文件,但代码将编写为仅接受具有 PDF 文件扩展名的项目。

准备工作

您应该能够访问一个 WordPress 开发环境,无论是在您的本地计算机上还是在远程服务器上,您将能够加载您的新插件文件。

如何操作...

  1. 导航到您开发安装的 WordPress 插件目录。

  2. 创建一个名为ch5-custom-file-uploader的新目录。

  3. 导航到该目录并创建一个名为ch5-custom-file-uploader.php的新文本文件。

  4. 在代码编辑器中打开新文件,并在插件文件顶部添加一个适当的标题,命名为“第五章 - 自定义文件上传器”。

  5. 添加以下行代码以注册一个在 WordPress 渲染帖子编辑器表单开头 HTML 代码时要执行的函数:

add_action( 'post_edit_form_tag', 'ch5_cfu_form_add_enctype' ); 
  1. 添加以下代码段以提供ch5_cfu_form_add_enctype函数的实现:
function ch5_cfu_form_add_enctype() {   
    echo ' enctype="multipart/form-data"';   
} 
  1. 插入以下行代码以注册一个在 WordPress 准备所有管理部分的元框时要调用的函数:
add_action( 'add_meta_boxes', 'ch5_cfu_register_meta_box' ); 
  1. 添加以下代码块以实现ch5_cfu_register_meta_box函数:
function ch5_cfu_register_meta_box() { 
    add_meta_box( 'ch5_cfu_upload_file', 'Upload File', 
                  'ch5_cfu_upload_meta_box', 'post', 'normal' ); 
    add_meta_box( 'ch5_cfu_upload_file', 'Upload File', 
                  'ch5_cfu_upload_meta_box', 'page', 'normal' ); 
} 
  1. 使用以下代码实现负责渲染元框内容的函数,ch5_cfu_upload_meta_box
function ch5_cfu_upload_meta_box( $post ) { ?> 
    <table> 
        <tr> 
            <td style="width: 150px">PDF Attachment</td> 
            <td> 
            <?php 
            // Retrieve attachment data for post 
            $attachment_data = get_post_meta( $post->ID, 
                                              'attach_data', 
                                              true ); 

            // Display post link if data is present 
            if ( empty ( $attachment_data ) ) { 
                echo 'No Attachment Present'; 
            } else { 
                echo '<a target="_blank" href="'; 
                echo esc_url( $attachment_data['url'] ); 
                echo '">' . 'Download Attachment</a>'; 
            }                 
            ?> 
            </td> 
        </tr> 
        <tr> 
            <td>Upload File</td> 
            <td><input name="upload_pdf" type="file" /></td> 
        </tr> 
        <tr> 
            <td>Delete File</td> 
            <td><input type="submit" name="delete_attachment" 
                       class="button-primary"
                       id="delete_attachment" 
                       value="Delete Attachment" /></td> 
        </tr> 
    </table> 
<?php } 
  1. 添加以下行代码,该代码调用add_action函数以注册在处理要保存的帖子数据时要执行的回调:
add_action( 'save_post', 'ch5_cfu_save_uploaded_file', 10, 2 ); 
  1. 使用以下代码实现ch5_cfu_save_uploaded_file
function ch5_cfu_save_uploaded_file( $post_id = false,  
                                     $post = false ) { 
    if ( isset($_POST['delete_attachment'] ) ) { 
        $attach_data = get_post_meta( $post_id, 'attach_data', 
                                      true ); 

        if ( !empty( $attach_data ) ) { 
            unlink( $attach_data['file'] ); 
            delete_post_meta( $post_id, 'attach_data' ); 
        } 
    } elseif ( 'post' == $post->post_type ||
               'page' == $post->post_type ) { 

        // Look to see if file has been uploaded by user 
        if( array_key_exists( 'upload_pdf', $_FILES ) && 
            !$_FILES['upload_pdf']['error'] ) { 

            // Retrieve file type and store lower-case version 
            $file_type_array = wp_check_filetype( basename( 
                 $_FILES['upload_pdf']['name'] ) );            
            $file_ext = strtolower( $file_type_array['ext'] );  

            // Display error message if file is not a PDF 
            if ( 'pdf' != $file_ext ) { 
                wp_die( 'Only files of PDF type are allowed.' ); 
                exit; 
            } else { 
                // Send uploaded file data to upload directory  
                $upload_return = wp_upload_bits( 
                    $_FILES['upload_pdf']['name'], null, 
                    file_get_contents( 
                    $_FILES['upload_pdf']['tmp_name'] ) ); 

                // Replace backslashes with slashes for Windows
                // web servers
                $upload_return['file'] =  
                    str_replace( '\\', '/', 
                                 $upload_return['file'] ); 

                // Set upload path data if successful. 
                if ( isset( $upload_return['error'] ) && 
                            $upload_return['error'] != 0 ) { 
                    $errormsg = 'There was an error uploading'; 
                    $errormsg .= 'your file. The error is: '; 
                    $errormsg .= $upload_return['error']; 
                    wp_die( $errormsg );   
                    exit; 
                } else { 
                    $attach_data = get_post_meta( $post_id,
                                                  'attach_data', 
                                                  true ); 

                    if ( !empty( $attach_data ) ) {
                        unlink( $attach_data['file'] ); 
                    } 
                    update_post_meta( $post_id, 'attach_data', 
                                      $upload_return ); 
                }  
            } 
        } 
    } 
} 
  1. 添加以下代码行以注册一个过滤器函数,当帖子内容和页面内容准备显示时将被调用:
add_filter( 'the_content', 'ch5_cfu_display_pdf_link' );
  1. 插入以下代码以提供ch5_cfu_display_pdf_link函数的实现:
function ch5_cfu_display_pdf_link ( $content ) { 
    $post_id = get_the_ID();

    if ( !empty( $post_id ) ) {
        if ( 'post' == get_post_type( $post_id ) ||
             'page' == get_post_type( $post_id ) ) {

            $attachment_data =
                get_post_meta( $post_id, 'attach_data', true ); 

            if ( !empty( $attachment_data ) ) { 
                $content .= '<div class="file_attachment">';
                $content .= '<a target="_blank" href="'; 
                $content .= esc_url( $attachment_data['url'] ); 
                $content .= '">' . 'Download additional '; 
                $content .= 'information</a></div>'; 
            } 
        }
    }
    return $content;
} 
  1. 保存并关闭插件文件。

  2. 导航到插件管理页面并激活第五章 - 自定义文件上传插件。

  3. 编辑您网站上任何帖子以查看新的上传文件元框,然后点击更新以保存帖子并将选定的 PDF 文件上传并关联到项目:

图片

  1. 查看帖子以查看下载附件文件的链接:

图片

它是如何工作的...

默认的 WordPress 帖子编辑器声明了一个没有定义编码类型的简单表单,因此只能接受常规文本输入。幸运的是,我们有访问动作钩子来注册一个回调函数,该函数将在表单创建时输出额外的代码,允许我们上传文件。此回调在此菜谱的第一部分实现。

下一个代码部分注册了一个元框,正如我们在许多先前的菜谱中看到的那样,用于显示一个新编辑器部分,该部分将显示一个指向附件文件的链接(如果存在),一个文件选择框用于上传新文件,以及一个按钮用于请求删除附件。

接下来,我们将转到负责处理帖子数据的函数,菜谱的代码首先检查用户是否请求删除与帖子关联的文件。如果是这种情况,它将进行文件删除并移除相关的帖子元数据。在其他情况下,如果项目的帖子类型是帖子或页面,插件将开始搜索用户在 PHP 全局$_FILES数组中上传的文件。此数组包含有关作为表单帖子数据处理部分的任何上传的信息。如果找到条目,我们将使用wp_check_filetype函数检索有关文件类型的信息,并继续将生成的扩展名转换为小写字符串,以便比较更容易。

由于此示例仅期望接收 PDF 文件,因此代码会检查文件扩展名是否正确,以决定是否使用wp_die函数显示错误消息,或者使用wp_upload_bits函数将文件从 Web 服务器的临时目录移动到 WordPress 中的wp-content/uploads目录。在后一种情况下,它还会将生成的文件路径和 URL 存储在帖子自定义字段表中。

完成此操作后,此菜谱实现了the_content的过滤器函数,用于在帖子下面和页面下面显示关联的 PDF 附件链接。

参见

  • 使用自定义元框添加额外字段到帖子编辑器的菜谱

第六章:接受用户内容提交

在本章中,我们将专注于向访客提供向网站提交内容的能力。我们将涵盖以下食谱:

  • 创建客户端内容提交表单

  • 在自定义帖子类型中保存用户提交的内容

  • 在新提交后发送电子邮件通知

  • 使用在线服务在用户表单上实现 CAPTCHA

  • 使用本地库在用户表单上实现 CAPTCHA

简介

给用户提供在网站上贡献内容的能力是吸引社区和保持网站内容新鲜的好方法。回到在第四章中实施的书籍评论系统,《自定义帖子类型的威力》,本章解释了如何允许访客向网站添加自己的书评。

创建客户端内容提交表单

向访客提供在网站上贡献内容的能力的第一步是展示一个他们可以使用来提交新内容的表单。本食谱展示了如何创建一个可以轻松插入任何 WordPress 页面以渲染简单表单的短代码。

准备工作

你应该运行在第四章中创建的 Book Reviews 插件的最终版本,即《自定义帖子类型的威力》,或者使用下载的代码包中的最终结果代码(Chapter 4/ch4-book-reviews/ch4-book-reviews-v11.php)。

如何操作...

  1. 导航到你的开发安装的 WordPress 插件目录。

  2. 创建一个名为ch6-book-review-user-submission的新目录并打开它。

  3. 创建一个名为ch6-book-review-user-submission.php的文本文件。

  4. 在代码编辑器中打开新文件,并在插件文件顶部添加一个适当的标题,将插件命名为“第六章 - 书评用户提交”。

  5. 添加以下代码行以声明一个新的短代码及其相关函数:

add_shortcode( 'submit-book-review', 'ch6_brus_book_review_form' ); 
  1. ch6_brus_book_review_form函数提供以下代码段:
function ch6_brus_book_review_form() { 
    // make sure user is logged in 
    if ( !is_user_logged_in() ) { 
        echo '<p>You need to be a website member to be able to '; 
        echo 'submit book reviews. Sign up to gain access!</p>'; 
        return; 
    } 
    ?> 

    <form method="post" id="add_book_review" action=""> 

    <!-- Nonce fields to verify visitor provenance --> 
    <?php wp_nonce_field( 'add_review_form', 'br_user_form' ); ?>

    <table> 
        <tr> 
            <td>Book Title</td> 
            <td><input type="text" name="book_title" /></td> 
        </tr> 
        <tr> 
            <td>Book Author</td> 
            <td><input type="text" name="book_author" /></td> 
        </tr> 
        <tr> 
            <td>Review</td> 
            <td><textarea name="book_review_text"></textarea></td> 
        </tr> 
        <tr> 
          <td>Rating</td> 
            <td><select name="book_review_rating"> 
            <?php 
            // Generate all rating items in drop-down list 
            for ( $rating = 5; $rating >= 1; $rating-- ) { ?> 
                <option value="<?php echo $rating; ?>"> 
                               <?php echo $rating; ?> stars 
            <?php } ?> 
            </select> 
            </td> 
        </tr> 
        <tr> 
            <td>Book Type</td> 
            <td> 
            <?php  
            // Retrieve array of all book types in system 
            $book_types = get_terms( 'book_reviews_book_type', 
                                 array( 'orderby' => 'name', 
                                        'hide_empty' => 0 ) ); 

            // Check if book types were found 
            if ( !is_wp_error( $book_types ) && 
                 !empty( $book_types ) ) { 

                echo '<select name="book_review_book_type">'; 

                // Display all book types 
                foreach ( $book_types as $book_type ) { 
                     echo '<option value="' . $book_type->term_id; 
                     echo '">' . $book_type->name . '</option>'; 
               }     

               echo '</select>'; 
            } ?> 
            </td> 
        </tr> 
    </table> 

    <input type="submit" name="submit" value="Submit Review" /> 
    </form> 
<?php } 
  1. 保存并关闭插件文件。

  2. 激活“第六章 - 书评用户提交”插件。

  3. 创建一个新页面,并在项目内容中插入新创建的[submit-book-review]短代码。

  4. 发布并查看页面以查看表单。如果你提交表单,目前不会有任何动作,因为我们还没有实现一个处理函数来解析和保存提交的数据:

图片

它是如何工作的...

如前几章所见,短代码是可以在任何帖子或页面中轻松添加的特殊文本块,当它们出现在用户访问的页面中时,会被内容替换。本食谱使用add_shortcode函数声明一个新的短代码,该短代码会被替换为评论提交表单。

该表单本身使用标准 HTML 创建,并显示多个文本字段。它还使用一些 PHP 代码来动态构建系统中定义的评分和书籍类型列表。表单还包括对wp_nonce_field函数的 PHP 调用,该函数在创建插件配置面板时已见过,用于添加将作为关联数据处理函数中的安全措施使用的隐藏字段。最后,代码检查访问该页面的用户是否已登录到网站,并在检查结果为否定时显示一条简短的消息而不是提交表单。

提交时,表单操作将发送访客内容到显示书籍评论表单的页面。此新内容将被拦截并在下一个配方中添加的代码中进行处理。

相关内容

  • 在第二章的插件框架基础中,创建一个新的简单短代码配方。

在自定义帖子类型中保存用户提交的内容

当访客点击上一个配方中创建的表单上的提交按钮时,表单的目标被设置为托管提交表单的同一页面。由于该页面无法处理表单数据,我们必须实现一个动作钩子来拦截这些帖子数据并将其发送到我们将定义的处理函数。本配方展示了如何实现一个负责处理用户输入的函数。

准备工作

您应该运行第四章中创建的最终版本的书籍评论插件,并且应该已经遵循了创建客户端内容提交表单的配方。或者,您可以从代码包中获取文件(Chapter 4/ch4-book-reviews/ch4-book-reviews-v11.phpChapter 6/ch6-book-review-user-submission/ch6-book-review-user-submission-v1.php),并将ch6-book-review-user-submission-v1.php重命名为ch6-book-review-user-submission.php

如何操作...

  1. 导航到您开发安装中 WordPress 插件文件夹的ch6-book-review-user-submission目录。

  2. 在文本编辑器中打开文件ch6-book-review-user-submission.php

  3. 将以下代码行添加以注册一个将拦截用户提交的书籍评论的函数:

add_action( 'template_redirect', 
            'ch6_brus_match_new_book_reviews' ); 
  1. 将以下代码块添加以实现ch6_brus_match_new_book_reviews函数:
function ch6_brus_match_new_book_reviews( $template ) { 
    if ( !empty( $_POST['ch6_brus_user_book_review'] ) ) { 
        ch6_brus_process_user_book_reviews(); 
    } else { 
        return $template; 
    } 
} 
  1. 插入以下代码以提供ch6_brus_process_user_book_reviews的实现:
function ch6_brus_process_user_book_reviews() { 
    // Check that all required fields are present and non-empty 
    if ( wp_verify_nonce( $_POST['br_user_form'], 
                          'add_review_form' ) && 
         !empty( $_POST['book_title'] ) && 
         !empty( $_POST['book_author'] ) && 
         !empty( $_POST['book_review_text'] ) &&  
         !empty( $_POST['book_review_book_type'] ) && 
         !empty( $_POST['book_review_rating'] ) ) { 
        // Create array with received data 
        $new_book_review_data = array( 
            'post_status' => 'publish',  
            'post_title' => $_POST['book_title'], 
            'post_type' => 'book_reviews', 
            'post_content' => $_POST['book_review_text'] ); 

        // Insert new post in website database 
        // Store new post ID from return value in variable 
        $new_book_review_id =  
                         wp_insert_post( $new_book_review_data ); 

        // Store book author and rating 
        add_post_meta( $new_book_review_id, 'book_author', 
                     wp_kses( $_POST['book_author'], array() ) ); 
        add_post_meta( $new_book_review_id, 'book_rating', 
                       (int) $_POST['book_review_rating'] ); 

        // Set book type on post 
        if ( term_exists( $_POST['book_review_book_type'], 
                          'book_reviews_book_type' ) ) { 
            wp_set_post_terms( $new_book_review_id, 
                               $_POST['book_review_book_type'], 
                               'book_reviews_book_type' );  
        } 
        // Redirect browser to book review submission page 
        $redirect_address = 
             ( empty( $_POST['_wp_http_referer'] ) ? site_url() : 
                                    $_POST['_wp_http_referer'] ); 
        wp_redirect( add_query_arg( 'add_review_message', '1', 
                     $redirect_address ) ); 
        exit; 
    } else { 
        // Display message if any required fields are missing 
        $abort_message = 'Some fields were left empty. Please '; 
        $abort_message .= 'go back and complete the form.'; 
        wp_die( $abort_message ); 
        exit; 
    } 
} 
  1. 在原始的ch6_brus_book_review_form函数中,在wp_nonce_field函数调用之后添加以下代码:
<?php if ( isset( $_GET['add_review_message'] ) 
            && $_GET['add_review_message'] == 1 ) { ?> 

    <div style="margin: 8px;border: 1px solid #ddd; 
        background-color: #ff0;"> 
    Thank for your submission! 
    </div> 

<?php } ?> 

<!-- Post variable to indicate user-submitted items --> 
<input type="hidden" name="ch6_brus_user_book_review" value="1" /> 
  1. 保存并关闭插件文件。

  2. 返回书籍评论提交表单并提交一条评论以将所有字段发送到新创建的处理函数。处理新内容后,脚本将返回到表单,显示一条确认消息。

工作原理...

在将书评表单数据提交到包含上一食谱中书评提交表单的页面后,这个食谱的前几个步骤将是将一个函数分配给template_redirect动作钩子,以便我们能够捕获新的书评内容。此钩子函数在 WordPress 处理序列的早期执行。如果找到,我们将调用在食谱其余部分定义的处理函数。

在我们的处理函数中,首先要做的是检查是否找到了作为帖子数据一部分的正确隐藏数据字段,使用wp_verify_nonce函数。如果不存在,表明有人可能试图在没有访问前端表单的情况下向网站发布数据,它将显示一个错误消息。

当我们确信我们的数据存储脚本是合法调用的,我们将继续通过首先检查所有字段是否存在且不为空来处理实际数据。如果不是这样,我们将显示一个错误消息,要求用户使用wp_die函数返回并完成表单。

如果所有字段都已正确接收,食谱将继续通过准备一个包含新提交的标题和评论文本、帖子状态以及book_reviews帖子类型名称的信息数组来处理传入的数据。生成的数组被发送到wp_insert_post函数以存储信息。正如我们所见,wp_insert_post只需要一个参数,该参数使用我们刚刚创建的数组来满足。虽然我们只定义了该数组的四个元素,但还有更多可用,这可以通过查阅 WordPress Codex(developer.wordpress.org/reference/functions/wp_insert_post/)来了解。

现在,调用wp_insert_post仅负责存储属于帖子数据的一些关键数据元素。我们必须随后调用update_post_metawp_set_post_terms来将剩余的用户信息存储到网站数据库中。

一旦所有信息都存储完毕,我们使用wp_redirectadd_query_arg函数的组合将用户送回到他提交书评的页面,同时确保目标地址中只有一个add_review_message变量的实例。

最后但同样重要的是,这个食谱对渲染书评表单的代码进行了微小修改,以添加一个确认消息,当插件接受信息时,该消息会显示给访客。

还有更多...

在一个充斥着垃圾邮件机器人和决心在任何网站上创建虚假内容的真实人的世界中,将新书评论立即显示在网站上可能不是明智之举。

审核用户提交的内容

与将新帖子条目的状态设置为发布相比,我们可以使用草稿值使新条目仅在后台管理区域可见。为了给插件用户提供更多灵活性,您还可以在配置面板中给他们提供选择他们偏好的方法的方式。

参见

  • 创建客户端内容提交表单配方

  • 在第三章,“用户设置和管理页面”中的处理和存储插件配置数据配方

  • 在第四章,“自定义文章类型的威力”中的向自定义文章类型编辑器添加新部分配方

在新提交时发送电子邮件通知

就像 WordPress 在发布新评论时向管理员发送电子邮件通知一样,当访客发布新书评时发送电子邮件,允许网站管理员在内容到来时进行审查,并决定是否批准将其发布到网上。

这个配方展示了如何准备电子邮件数据并使用wp_mail函数发送它。

准备工作

您应该运行在第四章,“自定义文章类型的威力”中创建的 Book Reviews 插件的最终版本,并且应该已经遵循了在自定义文章类型中保存用户提交的数据的配方(包括根据更多内容部分中的指示更改文章状态为草稿)。或者,您可以从代码包中获取结果文件(Chapter 4/ch4-book-reviews/ch4-book-reviews-v11.phpChapter 6/ch6-book-review-user-submission/ch6-book-review-user-submission-v2.php),并将ch6-book-review-user-submission-v2.php重命名为ch6-book-review-user-submission.php。最后,您应该能够访问托管在 Web 服务器上的 WordPress 安装,因为当在本地安装上运行时,通常不会发送电子邮件。确保您有权访问与网站管理员关联的电子邮件账户,以查看生成的电子邮件。

如何做到这一点...

  1. 导航到您的开发安装中 WordPress 插件文件夹的ch6-book-review-user-submission目录。

  2. 在文本编辑器中打开ch6-book-review-user-submission.php文件。

  3. 插入以下代码行以注册一个在提交新帖子时被调用的函数:

add_action( 'wp_insert_post', 'ch6_brus_send_email', 10, 2 ); 
  1. 插入以下代码块以实现ch6_brus_send_email函数:
function ch6_brus_send_email( $post_id, $post ) { 

    // Only send emails for user-submitted book reviews 
    if ( isset( $_POST['ch6_brus_user_book_review'] ) && 
         'book_reviews' == $post->post_type ) { 

        $admin_mail = get_option( 'admin_email' ); 
        $headers = 'Content-type: text/html'; 
        $message = 'A user submitted a new book review to your '; 
        $message .= 'WordPress website database.<br /><br />'; 
        $message .= 'Book Title: ' . $post->post_title ; 
        $message .= '<br /><a href="'; 
        $message .= add_query_arg( array( 
                                   'post_status' => 'draft', 
                                  'post_type' => 'book_reviews' ), 
                                   admin_url( 'edit.php' ) ); 
        $message .= '">Moderate new book reviews</a>'; 
        $email_title = htmlspecialchars_decode( get_bloginfo(), 
             ENT_QUOTES ) . " - New Book Review Added: " . 
             htmlspecialchars( $_POST['book_title'] ); 
        // Send email 
        wp_mail( $admin_mail, $email_title, $message, $headers ); 
    } 
} 
  1. 保存并关闭插件文件。

  2. 返回书评提交表单并提交书评。将向与网站管理员关联的地址发送一封电子邮件,其中包含新评论的一些信息:

图片

它是如何工作的...

wp_mail函数可以被任何插件用来发送电子邮件消息。它需要五个参数来定义出站消息的所有元素:

wp_mail( $destination, $subject, $message, [$headers], 
         [$attachments] ); 

前三个参数是必需的,分别定义了预期收件人的电子邮件地址、消息标题和内容。正如我们在本配方中看到的,内容主要使用标准 HTML 语法指定,而目标电子邮件地址是通过get_option函数从选项表中检索的。至于标题,它是由多个文本元素(如博客标题和书评标题)构建的,以创建最终结果。

下一个参数是可选的,为电子邮件提供标题信息,其中该部分最重要的信息是字符集。最后一个参数可以可选地用来指定一个或多个文件的路径,这些文件将被作为电子邮件附件发送。

为了使网站管理员更容易管理新条目,消息正文的一部分包含一个链接到 WordPress 网站管理区域的自定义帖子管理页面,以便快速显示所有未批准的条目(设置为草稿项目)。

参考内容

  • 创建客户端内容提交表单配方

在用户表单上使用在线服务实现 CAPTCHA

网站表单上的一种常见安全措施是使用 CAPTCHA 代码,其中显示扭曲的字母或其他形式的测试,以检查提交数据的个人不是垃圾邮件机器人。我们一直在构建的接受访客提交的书评表单可以从这种技术中受益,以避免筛选不想要的条目。

这个配方展示了如何将 Google 的 reCAPTCHA 服务集成到我们的书评提交表单中。如果您更喜欢使用本地 CAPTCHA 脚本以避免依赖在线服务或确保您的表单可以在所有国家使用,请跳转到下一个配方,标题为使用本地库在用户表单上实现 CAPTCHA

准备工作

您应该运行在第四章中创建的最终版本的“书评插件”,自定义帖子类型的力量,并且应该已经遵循了在新的提交后发送电子邮件通知的配方。或者,您可以从代码包中获取结果文件(Chapter 4/ch4-book-reviews/ch4-book-reviews-v11.phpChapter 6/ch6-book-review-user-submission/ch6-book-review-user-submission-v3.php),并在开始配方之前将ch6-book-review-user-submission-v3.php重命名为ch6-book-review-user-submission.php

这个配方需要有一个 Google 账户才能注册 reCAPTCHA 服务。

如何操作...

  1. 访问 Google reCAPTCHA 管理员网站(www.google.com/recaptcha/admin),如果您尚未连接到 Google 服务,请登录。

  2. 通过指定标签(例如,开发网站)并使用单选选择器设置 reCAPTCHA 类型为 reCAPTCHA V2 来注册一个新的网站。

  3. 在域名字段中输入您将用于测试代码的 Web 服务器的域名。如果您正在运行本地开发服务器,它可能是localhost

  4. 打开复选框以接受服务条款,然后点击注册以完成新网站创建过程。

  5. 在生成的页面中,注意服务分配给您的测试网站的网络和密钥。

  6. 导航到您的插件文件夹中的ch6-book-review-user-submission目录,并在代码编辑器中打开名为ch6-book-review-user-submission.php的文件。

  7. 在文件末尾插入以下代码行以注册一个函数,该函数将在 WordPress 准备在网站上加载的脚本列表时被调用:

add_action( 'wp_enqueue_scripts', 'ch6_brus_recaptcha_script' );
  1. 添加以下代码块以提供ch6_brus_recaptcha_script函数的实现:
function ch6_brus_recaptcha_script() {
    wp_enqueue_script( 'google_recaptcha', 
                       'https://www.google.com/recaptcha/api.js',
                       array(), false, true );
}
  1. 在您的代码中找到ch6_brus_book_review_form函数,并在表单表中添加一行额外的行以显示 CAPTCHA,将**[my-website-key]**替换为从 reCAPTCHA 服务中较早获得的网站密钥:
<tr>
    <td colspan="2">
        <div class="g-recaptcha" 
             data-sitekey="[my-website-key]"></div>
    </td>
</tr>
  1. 保存您的插件并访问您的书评提交页面。现在你应该在表单中看到 Google reCAPTCHA 框出现了。

图片

  1. 访问 Google reCAPTCHA GitHub 页面(github.com/google/recaptcha)并将存储库的最新版本下载到您的计算机上。

  2. 提取存储库内容并将生成的src文件夹复制到ch6-book-review-user-submission目录。

  3. src文件夹重命名为recaptcha

  4. 在您的代码中找到ch6_brus_process_user_book_reviews函数,并在函数实现的顶部添加以下代码行,将[my-secret-key]替换为从 reCAPTCHA 服务中较早获得的密钥:

require_once plugin_dir_path( __FILE__ ) .
             '/recaptcha/autoload.php';

$recaptcha = new \ReCaptcha\ReCaptcha( '[my-secret-key]' ); 
$resp = $recaptcha->verify( $_POST['g-recaptcha-response'],
                            $_SERVER['REMOTE_ADDR'] );

if ( ! $resp->isSuccess() ) {
    // Display message if any required fields are missing 
    $abort_message = 'Missing or incorrect captcha.<br />'; 
    $abort_message .= 'Please go back and try again.'; 
    wp_die( $abort_message ); 
    exit; 
} else {
  1. ch6_brus_process_user_book_reviews函数的末尾之前添加一个额外的闭合括号(}),以关闭上一步插入的代码的else部分。

  2. 保存并关闭代码文件。

  3. 提交一个新的书评而不检查 reCAPTCHA 复选框,以查看它将不会被处理函数接受。

它是如何工作的...

Google 的 reCAPTCHA 服务是一项免费服务,可以在您的表单中生成和显示 CAPTCHA 框。随着时间的推移,它已经从要求用户在图像中解读乱码信息发展到显示地址和其他街道标志,并要求用户输入他们所看到的。在其最新版本中,reCAPTCHA 仅要求用户勾选一个框以表明他们不是机器人,并且只有在怀疑访客是自动化系统时才会提出更高级的问题。当访客勾选框时,一个隐藏字段会被填充一个验证码,该验证码会与表单数据的其余部分一起发送到我们的数据处理函数。

将 reCAPTCHA 添加到我们的表单实际上非常简单,只需要我们加载一个 JavaScript 脚本并在我们的表单中添加一行代码,该代码会被转换成服务的商标复选框。一旦表单数据提交,验证它比我们之前使用的非 ces 更复杂,因为我们的插件需要与 Google 的服务器通信以检查验证码。幸运的是,Google 提供了一个易于使用的库来隐藏许多操作复杂性。如果收到的 reCAPTCHA 代码有效,之前创建的数据处理和存储代码将按之前的方式执行。否则,将向用户显示错误消息。除了易于集成之外,使用第三方服务的另一个好处是大多数代码更新都是由服务提供商完成的。你仍然需要检查 PHP 验证库的偶尔更新,但这只是该服务功能的一小部分。

如果你计划分发一个用于多人或客户的 reCAPTCHA 服务的插件,那么在最终的插件代码中留下你自己的网站和密钥是没有意义的,就像我们在这里所做的那样。相反,你应该创建一个管理面板,就像你在第三章用户设置和管理页面中学到的那样,以便用户可以输入他们自己的密钥,并在网站上使用它们。

应该注意的是,虽然 Google reCAPTCHA 是最受欢迎的 CAPTCHA 生成服务之一,但在撰写本文时,它对中国访客不起作用,并且已经有一段时间没有工作了。如果你正在为广泛的受众开发插件,你可能需要考虑支持多个 CAPTCHA 服务或库,以确保用户无论身在何处都有选择。阅读下一个配方,了解使用本地库生成 CAPTCHA 的示例。

参见

  • 在新提交时发送电子邮件通知 的配方

  • 在设置菜单中创建管理页面菜单项 的配方

  • 使用 HTML 渲染管理页面内容 的配方

  • 处理和存储插件配置数据 的配方

  • 在保存选项时显示确认消息 的配方

在用户表单上使用本地库实现 CAPTCHA

如前一个配方中所述,使用在线服务在用户表单上实现 CAPTCHA,向面向访客的表单添加 CAPTCHA 有助于减少对网站的恶意提交。在了解了如何集成在线第三方服务后,这个配方展示了如何下载并集成一个本地 PHP 脚本来本地生成和验证 CAPTCHA 图像。

准备工作

你应该运行在第四章中创建的最终版本的“图书评论”插件,即《自定义文章类型的威力》,并且应该已经遵循了“在新提交时发送电子邮件通知”的步骤。或者,你也可以从代码包中获取结果文件(Chapter 4/ch4-book-reviews/ch4-book-reviews-v11.phpChapter 6/ch6-book-review-user-submission/ch6-book-review-user-submission-v3.php),在开始步骤之前将 ch6-book-review-user-submission-v3.php 重命名为 ch6-book-review-user-submission.php

为了能够生成 CAPTCHA 图像,需要在你的开发网络服务器的 PHP 安装中安装并激活 GD 和 FreeType 库。大多数在第一章《准备本地开发环境》中提到的预包装本地网络服务器都带有这些库的激活。它们也通常在大多数托管网络服务器上启用,但在分发包含以下代码的插件给更广泛的受众时,还应提及这些库是必需的。

如何操作...

  1. 访问 Securimage 网站 (www.phpcaptcha.org/download/) 下载他们 CAPTCHA 库的最新版本。

  2. 打开生成的存档,并将整个 securimage 目录提取到你的插件目录的 ch6-book-review-user-submission 文件夹中。

  3. 在插件目录中,使用代码编辑器打开名为 ch6-book-review-user-submission.php 的文件。

  4. 在你的代码中定位到 ch6_brus_book_review_form 函数,并在表单中添加一行以显示 CAPTCHA:

<tr>
    <td>Enter text from image</td>
    <td>
        <img id="captcha" 
             src="img/>                            __FILE__ ); ?>"
             alt="CAPTCHA Image" />
        <input type="text" name="captcha_code"
               size="10" maxlength="6" />
        <a href="#" 
           onclick="document.getElementById( 'captcha' ).src = 
           '<?php echo plugins_url(
                       '/securimage/securimage_show.php', 
                       __FILE__ ); ?>?'
           + Math.random(); return false">[ Different Image ]</a>
    </td>
</tr> 
  1. 保存你的插件并访问你的图书评论提交页面。你现在应该会在表单中看到 CAPTCHA 图像:

  1. 在你的代码中定位到 ch6_brus_process_user_book_reviews 函数,并在函数实现的顶部添加以下代码行:
if ( PHP_SESSION_NONE == session_status() ) {
    session_start();
}

include_once plugin_dir_path( __FILE__ ) .
             '/securimage/securimage.php';
$securimage = new Securimage();

if ( false == $securimage->check( $_POST['captcha_code'] ) ) {
    // Display message if any required fields are missing 
    $abort_message = 'Missing or incorrect captcha.<br />'; 
    $abort_message .= 'Please go back and try again.'; 
    wp_die( $abort_message ); 
    exit; 
} else {
  1. ch6_brus_process_user_book_reviews 函数的末尾之前添加一个额外的闭合括号(}),以关闭之前步骤中插入的代码的 else 部分。

  2. 保存并关闭代码文件。

  3. 提交一个新的图书评论而不输入 CAPTCHA 代码,以查看它将不会被处理函数接受。

工作原理...

Securimage 脚本是一个简单的工具,可以生成和显示 CAPTCHA 图像,以及使用会话数据存储它生成的字符串。

该配方的代码通过使用标准 HTML img 标签来显示 CAPTCHA 图像,该标签使用 securimage 目录中的某个脚本作为图像路径。在数据处理方面,我们的代码首先通过调用 session_start 来重新连接到由图像生成器启动的会话,以存储 CAPTCHA 代码。然后,它继续检查用户输入的 CAPTCHA 文本是否与显示的图像匹配,这是通过 securimage 类的 check 方法实现的。根据这个结果,我们显示错误消息或继续验证其他所需数据字段是否已正确提交。

虽然有许多 PHP 脚本可用于生成 CAPTCHA 图像,但这个特定的配方选择了它,因为它是一个开源脚本,使用的是 BSD 许可证,这与 WordPress 许可证兼容。这意味着你将能够将此脚本与你的插件一起在官方 WordPress 插件仓库中分发。

当你在插件中使用第三方库时,你应该定期检查该库是否已被其作者更新,并尽快将新版本纳入你的工作中,以确保你的插件不会暴露于安全漏洞。

对于更高级的内容过滤方法,请查阅 Akismet API (akismet.com/development/api/)。

参见

  • 使用在线服务在用户表单上实现 CAPTCHA 的配方

第七章:自定义用户数据

本章将向我们展示如何增强 WordPress 用户编辑器,并通过以下主题使用与用户关联的附加数据:

  • 向用户编辑器添加自定义字段

  • 处理和存储用户自定义数据

  • 在用户列表页面显示新用户数据

  • 在包含短代码中使用自定义用户数据

简介

在学习了如何使用自定义数据字段自定义帖子编辑器和分类之后,我们将要添加的下一个区域是用户创建和编辑工具。这种添加可以用于许多不同的目的,包括跟踪用户最后一次访问网站的时间或存储他们对感兴趣的服务类型的额外数据。本章在第二章插件框架基础和第三章用户设置和管理页面中,通过添加一个二级内容保护层来扩展我们在第二章开始的私有内容插件,该保护层将限制一些帖子或页面,使其仅对付费用户可用。

向用户编辑器添加自定义字段

与分类编辑器类似,用户编辑器允许开发者通过动作钩子添加额外字段,并通过使用相同的 HTML 结构来简化这一任务,无论是创建新用户还是修改现有用户。本配方展示了如何将回调函数分配给用户编辑器以添加新部分。

准备工作

要创建一个包含样式表和管理面板的完整插件,您应该已经遵循了第三章中的将样式表数据存储在用户设置配方,以获得本配方的起点。或者,您可以从下载的代码包中获取结果代码(Chapter 3/ch2-private-item-text-edit-stylesheet/ch2-private-item-text.php),并将ch2-private-item-text-edit-stylesheet重命名为ch2-private-item-text

也可能只是创建一个新的空插件文件,通过在您的插件目录中创建一个名为ch2-private-item-text的文件夹,并在其中添加一个名为ch2-private-item-text.php的文本文件,只添加一个标准的插件头部来遵循本章的配方。

如何操作...

  1. 导航到您开发安装的 WordPress 插件目录中的ch2-private-item-text文件夹。

  2. 在文本编辑器中打开ch2-private-item-text.php文件。

  3. 在文件末尾添加以下代码片段以声明一个全局变量并初始化变量的内容:

global $user_levels;
$user_levels = array( 'regular' => 'Regular', 'paid' => 'Paid' );
  1. 将以下代码行添加到注册一个函数,当通过管理界面添加新用户或编辑用户时调用:
add_action( 'user_new_form', 'ch2pit_show_user_profile' );
add_action( 'edit_user_profile', 'ch2pit_show_user_profile' );
  1. 将以下代码块添加以提供ch2pit_show_user_profile函数的实现:
function ch2pit_show_user_profile( $user ) {
    global $user_levels;
    if ( 'add-new-user' == $user ) {
        $current_user_level = '';
    } elseif ( !empty( $user ) && isset( $user->data->ID ) ) {
        $user_id = $user->data->ID;
        $current_user_level =
            get_user_meta( $user_id, 'user_level', true );
    } ?> 

    <h3>Site membership level</h3>
    <table class="form-table">
        <tr>
            <th>Level</th>
            <td><select name="user_level" id="user_level">
            <?php foreach( $user_levels as 
                           $user_level_index => $user_level ) { ?>
            <option value="<?php echo $user_level_index; ?>"
            <?php selected( $current_user_level,
                            $user_level_index ); ?>>
            <?php echo $user_level; ?></option>
            <?php } ?>
            </select></td>
        </tr>
    </table> 
<?php }
  1. 保存并关闭插件文件。

  2. 前往管理界面的插件部分,并确保已激活 第二章 - 私人物品文本 插件。

  3. 在用户部分下选择“添加新用户”菜单项,查看用户创建页面,并查看表单底部的新的网站会员级别部分:

图片

  1. 编辑网站上的现有用户之一,以查看附加部分也会显示。如果您在任一情况下尝试保存用户,新添加的字段将不会保存,直到您执行下一个配方中的步骤。

工作原理...

user_new_formedit_user_profile 动作钩子允许开发者在向网站添加新用户和编辑用户时向用户编辑器添加内容。

WordPress 提供了第三个动作钩子,称为 show_user_profile。与该钩子关联的函数在用户查看自己的个人资料时会被调用。您应仔细考虑是否根据您插件的本性和要存储的附加信息,注册一个与该动作钩子关联的函数。换句话说,如果您不注册任何函数与该钩子,就像我们在本配方中所做的那样,您将不会在查看网站上的个人资料时看到创建的附加字段,即使您是管理员。您仍然能够看到付费内容,因为我们将会向具有管理员权限的用户显示它。

我们的回调函数首先检查是否通过 $user 参数接收到了有效的用户,或者是否正在创建新用户。根据结果,它通过调用 get_user_meta 函数检索相关的用户级别数据,或者初始化一个空变量。get_user_meta 的语法与 get_post_metaget_term_meta 函数的语法相同,第一个参数用于指定用户 ID,其余字段表示要检索的字段名称以及数据是否应作为单个变量或数组返回:

get_user_meta( $user_id, $field_name, $single );

我们的其他回调函数负责渲染一个基本的 HTML 选择列表,并根据当前用户的会员级别选择列表中的一个项目。

在本配方中,我们唯一添加的额外元素是将用户会员级别数组声明为一个 全局 变量,我们将在本章的多个函数中引用它。

参见

  • 将样式表数据存储在用户设置中 的配方

处理和存储用户自定义数据

当用户资料被创建或更新时,WordPress 提供了一种简单的方法来注册一个回调函数,以便在网站的数据库中保存自定义用户数据。本配方展示了如何使用这个动作钩子。

准备工作

您应该已经遵循了 向用户编辑器添加自定义字段 的配方,以便为本配方和结果插件提供一个起点,并且结果插件仍然在您的开发站点中处于活动状态。或者,您可以从下载的代码包中获取结果代码(Chapter 7/ch2-private-item-text/ch2-private-item-text-v3.php),将其重命名为 ch2-private-item-text.php

如何操作...

  1. 导航到您开发安装的 WordPress 插件目录中的 ch2-private-item-text 文件夹。

  2. 在文本编辑器中打开 ch2-private-item-text.php 文件。

  3. 在文件末尾添加以下代码行以注册在用户数据在初始创建时或用户更新时被调用的函数:

add_action( 'user_register', 'ch2pit_save_user_data' );
add_action( 'profile_update', 'ch2pit_save_user_data' );
  1. 将以下代码块添加到提供 ch2pit_save_user_data 函数实现的代码中:
function ch2pit_save_user_data( $user_id ) {
    global $user_levels;
    if ( isset( $_POST['user_level'] ) &&
         !empty( $_POST['user_level'] ) &&
         array_key_exists( $_POST['user_level'], 
                           $user_levels ) ) {
        update_user_meta( $user_id, 'user_level',
                          $_POST['user_level'] );
    } else {
        update_user_meta( $user_id, 'user_level', 'regular' );
    }
}
  1. 保存并关闭插件文件。

  2. 编辑网站上现有的用户或创建一个新用户,以查看用户级别值是否已保存。

工作原理...

类似于向用户编辑器添加字段,WordPress 提供了两个不同的动作钩子来注册在用户最初注册时(user_register)和用户资料更新时(profile_update)被调用的函数。如您所见,我们在两种情况下都注册了相同的回调函数,其中我们检查是否收到了表示所需用户级别的值,并使用 update_user_meta 函数来保存它。类似于文章和术语元更新函数,此函数有三个参数,用于指示应将信息关联到的用户 ID、自定义数据字段的名称以及要存储的值:

update_user_meta( $user_id, $field_name, $single );

作为额外的预防措施,我们使用 PHP 的 array_key_exists 函数检查我们收到的值是否存在于我们的全局用户级别数组中。

if 语句的 else 分支被放置在那里,以在字段不存在时设置默认用户级别,这可能发生在网站允许访客自行注册账户的情况下。

当然,将用户分类为普通用户或付费用户的当前过程非常手动,需要网站管理员登录到网站的仪表板并逐个更新他们的状态。一个更完善的解决方案可能是根据通过在线支付服务向网站支付的费用来设置此字段的值。

参见

  • 向用户编辑器添加自定义字段 的配方

在用户列表页面显示新用户数据

随着新字段添加到用户资料中,对于网站管理员来说,在网站用户列表中查看这些数据并且能够过滤用户以仅显示被分配了特定值的用户非常有用。本配方展示了如何向 WordPress 管理区域的用户列表添加额外列,以及如何添加一个筛选函数以减少显示的记录数。

准备工作

你应该已经遵循了处理和存储用户自定义数据的配方,以便为这个配方提供一个起点,并且生成的插件仍然在你的开发网站上处于活动状态。或者,你可以从下载的代码包中获取生成的代码(第七章/ch2-private-item-text/ch2-private-item-text-v4.php),并将其重命名为ch2-private-item-text.php

如何操作...

  1. 导航到你的开发安装的 WordPress 插件目录中的ch2-private-item-text文件夹。

  2. 在文本编辑器中打开ch2-private-item-text.php文件。

  3. 添加以下代码行以注册一个在 WordPress 准备在用户列表页面上显示的列列表时被调用的函数:

add_filter( 'manage_users_columns', 'ch2pit_add_user_columns' );
  1. 使用以下代码块为ch2pit_add_user_columns函数添加一个实现:
function ch2pit_add_user_columns( $columns ) {
    $new_columns = array_slice( $columns, 0, 2, true ) +
                   array( 'level' => 'User Level' ) +
                   array_slice( $columns, 2, NULL, true );
    return $new_columns;
}
  1. 添加以下代码行以分配一个在 WordPress 准备显示所有用户列表的每个列的内容时被调用的函数:
add_filter( 'manage_users_custom_column', 
            'ch2pit_display_user_columns_data', 10, 3 );
  1. 通过插入以下代码段为ch2pit_display_user_columns_data函数提供一个实现:
function ch2pit_display_user_columns_data( $val, $column_name,
                                           $user_id ) {
    global $user_levels;
    if ( 'level' == $column_name ) {
        $current_user_level = get_user_meta( $user_id, 
                                             'user_level', true );
        if ( !empty( $current_user_level ) ) {
            $val = $user_levels[$current_user_level];
        }
    }
    return $val;
}
  1. 添加以下代码片段以注册一个在 WordPress 准备管理员可以在用户列表页面上执行的操作列表时被调用的函数:
add_action( 'restrict_manage_users', 'ch2pit_add_user_filter' );
  1. 将此代码段添加到为ch2pit_add_user_filter函数提供一个主体的代码:
function ch2pit_add_user_filter() {
    global $user_levels;
    $filter_value = '';
    if ( isset( $_GET['user_level'] ) ) {
        $filter_value = $_GET['user_level'];
    } ?>

    <select name="user_level" class="user_level" 
            style="float:none;">
    <option value="">No filter</option>
    <?php foreach( $user_levels as
                       $user_level_index => $user_level ) { ?>
        <option value="<?php echo $user_level_index; ?>" 
        <?php selected( $filter_value, $user_level_index ); ?>>
        <?php echo $user_level; ?></option>
    <?php } ?>
    <input type="submit" class="button" value="Filter">
<?php }
  1. 添加以下代码行以注册一个在 WordPress 渲染管理页面页脚时被调用的函数:
add_action( 'admin_footer', 'ch2pit_user_filter_js' );
  1. 通过添加以下代码段为ch2pit_user_filter_js函数提供一个实现:
function ch2pit_user_filter_js() {
    global $current_screen;
    if ( 'users' != $current_screen->id ) {
        return;
    } ?>

    <script type="text/javascript">
        jQuery( document ).ready( function() {
            jQuery( '.user_level' ).first().change( function() {
                jQuery( '.user_level' ).
                    last().val( jQuery( this ).val() );
            });

            jQuery( '.user_level' ).last().change( function() {
                jQuery( '.user_level' ).
                    first().val( jQuery( this ).val() );
            });
        });
    </script>
<?php }
  1. 添加此代码行以注册一个在 WordPress 准备从数据库检索用户列表的查询时将被执行的函数:
add_filter( 'pre_get_users', 'ch2pit_filter_users' );
  1. 插入以下代码块以定义ch2pit_filter_users函数:
function ch2pit_filter_users( $query ) {
    global $pagenow;
    global $user_levels;

    if ( is_admin() && 'users.php' == $pagenow && 
         isset( $_GET['user_level'] ) ) {
        $filter_value = $_GET['user_level'];
        if ( !empty( $filter_value ) && 
             array_key_exists( $_GET['user_level'], 
                               $user_levels ) ) {
            $query->set( 'meta_key', 'user_level' );
            $query->set( 'meta_query', array(
                         array( 'key' => 'user_level',
                                'value' => $filter_value ) ) );
        } 
    }
}
  1. 保存并关闭插件文件。

  2. 访问管理界面的用户部分以查看新的用户级别列。

图片

  1. 使用过滤器下拉列表并点击“过滤”以限制显示的记录数。

它是如何工作的...

本配方中的第一个代码段是一个过滤器函数,它获取系统打算在管理界面用户部分显示的所有列的列表。虽然我们可以在数组的末尾添加我们的新条目,就像我们在第四章的在自定义帖子列表页面中显示额外列配方中所做的那样,自定义帖子类型的威力,但我们在这个配方中采用了一种更高级的方法;我们使用 PHP 的array_slice函数将列数组分成两部分,并在现有项目之间插入我们的新列名。

我们通过提供一个显示分配给每个用户的用户级别的函数来继续这个配方。这是通过简单地根据当前用户 ID 检索信息并将要显示的数据返回来完成的。需要注意的是,用户管理页面采取的方法与文章和页面自定义列内容略有不同,因为它使用了一个过滤器函数,我们在其中返回数据而不是使用直接将内容发送到浏览器的动作钩子。

下一个代码段负责渲染一个用户级别的下拉列表,可以用来过滤用户列表;这是通过使用标准的 HTML 和代码遍历用户级别列表并显示它们来完成的。不幸的是,WordPress 通过在列出用户之前和之后调用我们的函数两次来使事情复杂化,导致同一页面上出现两个下拉列表,无法区分它们。为了避免两个下拉列表设置不同的值并将这些冲突的值发送到我们的过滤代码中,下一个函数将 JavaScript 代码放置在所有用户列表页面的页面页脚中。这允许我们在用户与之交互时同步任一下拉列表的值。

我们的最后一步是在显示用户列表时,如果我们在页面 URL 中找到一个过滤参数,就修改用户查询。如果找到参数并且页面是正确的,我们将检查该值是否是我们用户级别数组的一部分,然后将其添加到系统的当前查询变量中。

参见

  • 处理和存储用户自定义数据配方

在包含短代码中使用自定义用户数据

现在我们已经在用户配置文件中存储了额外的数据,并简化了在管理页面中查看这些信息,要实现将一些网站内容限制为付费用户的目标,唯一缺少的组件是引入一个新的包围短代码,该短代码在显示内容之前将检查用户的级别。配方将展示如何创建这个新的短代码。

准备工作

你应该已经遵循了在用户列表页面显示新用户数据的配方,以便为这个配方提供一个起点,并且生成的插件仍然在你的开发站点中处于活动状态。或者,你可以从第七章的下载代码包中获取生成的代码(Chapter 7/ch2-private-item-text/ch2-private-item-text-v5.php),将其重命名为ch2-private-item-text.php

如何做到这一点...

  1. 导航到你的开发安装的 WordPress 插件目录中的ch2-private-item-text文件夹。

  2. 在文本编辑器中打开ch2-private-item-text.php文件。

  3. 在定义一个新的短代码并定义当使用它时调用的函数时,添加以下代码行:

add_shortcode( 'paid', 'ch2pit_paid_shortcode' );
  1. 提供以下代码段的ch2pit_paid_shortcode实现:
function ch2pit_paid_shortcode( $atts, $content = null ) {
    if ( is_user_logged_in() ) {
        $current_user = wp_get_current_user();
        $current_user_level = get_user_meta( $current_user->ID, 
                                             'user_level', true );
        if ( 'paid' == $current_user_level ||  
             current_user_can( 'activate_plugins' ) ) {
            return '<div class="paid">' . $content . '</div>';
        }
    }
    $output = '<div class="register">';
    $output .= 'You need to be a paid member to access ';
    $output .= 'this content.</div>';
    return $output; 
}
  1. 保存并关闭插件文件。

  2. 创建一个新的帖子,并用 [paid][/paid] 标签包裹部分或全部内容。以管理员、未登录的访问者、注册的普通用户和注册的付费用户身份查看页面,以验证内容仅在第一种和最后一种情况下显示。

图片

它是如何工作的...

与我们在第二章中创建的 创建新的封装短代码 菜单中的 [private] 短代码类似,这个菜谱中设置的新的 [paid] 短代码可以让内容创建者识别一篇文章或页面中的一或多个只能由设置为付费会员的用户查看的部分。

要做到这一点,我们的短代码实现首先检查访问者是否已登录。如果是这样,它将进一步通过请求 WordPress 提供当前用户的数据来验证用户,然后使用用户 ID 检索我们在本章前面的菜谱中存储的 user_level 元数据。或者,它使用 current_user_can WordPress 函数并指定 'activate_plugins' 作为参数来检查用户是否是网站管理员,因为只有管理员才能执行此操作。如果所有这些条件都满足,则将显示封装的帖子内容。否则,将显示一条消息,表明用户必须是付费会员才能查看此内容。

参见

  • 在用户列表页面显示新用户数据 菜单

第八章:创建自定义 MySQL 数据库表

在本章中,我们将围绕创建自定义数据库表涵盖以下主题:

  • 创建新的数据库表

  • 在插件移除时删除自定义表

  • 在插件升级时更新自定义表结构

  • 在管理页面上显示自定义表数据

  • 在自定义表中插入和更新记录

  • 从自定义表中删除记录

  • 在短代码中显示自定义数据库表数据

  • 实现一个搜索功能以检索自定义表数据

  • 从用户文件导入数据到自定义表中

简介

如第四章《自定义文章类型的威力》中所述,自定义文章类型提供了一种非常强大且简单的方法来在 WordPress 安装中创建和管理自定义内容。话虽如此,如果您希望创建的新项目不受益于内置文本编辑器的访问,并且有大量需要存储在系统中的数据字段,那么使用自定义文章类型来存储它们实际上可能会变得繁琐。更具体地说,每个自定义字段都需要一个单独的函数调用与自定义文章相关联。此外,自定义字段的功能有限,因为它们将所有信息存储在简单的文本字段中,这使得根据特殊数据类型(如日期)执行有序查询变得困难。

管理自定义内容的一个替代方案是在网站的数据库中创建新表,并提供一个自定义界面来管理这些新项目。

虽然直接与网站数据库打交道听起来可能是一项艰巨的任务,并且实际上只有在自定义文章类型无法按预期工作的情况下才应该这样做,但 WordPress 实际上提供了一个实用工具类,它使得创建新数据库表、在这些新结构中存储信息以及执行数据检索查询变得非常容易。虽然具备基本的结构化查询语言SQL)知识将有助于我们创建错误跟踪系统时理解本章中的所有食谱,但每个食谱都详细解释了每个命令如何工作以产生最终结果。

创建新的数据库表

将要存储在自定义数据库表中的自定义数据元素的创建的第一步是创建该表本身。这是通过准备一个标准的 SQL 命令来完成的,该命令指定了表名及其所需的结构,然后让 WordPress 在网站数据库上执行它。

本食谱展示了如何准备和执行一个查询,创建一个用于存储错误报告的表。

准备工作

您应该能够访问 WordPress 开发环境,无论是在您的本地计算机上还是在远程服务器上,您将能够加载您的新插件文件。

如何做到这一点...

  1. 导航到您开发安装的 WordPress 插件目录。

  2. 创建一个名为 ch8-bug-tracker 的新目录。

  3. 导航到目录并创建一个名为 ch8-bug-tracker.php 的文本文件。

  4. 在代码编辑器中打开新文件,并在插件文件顶部添加一个合适的标题,将插件命名为“第八章 - Bug Tracker”。

  5. 添加以下代码行以注册一个在插件激活时被调用的函数:

register_activation_hook( __FILE__, 'ch8bt_activation' ); 
  1. 添加以下代码段以提供ch8bt_activation函数的实现:
function ch8bt_activation() { 
    // Get access to global database access class 
    global $wpdb; 

    // Create table on main blog in network mode or single blog 
    ch8bt_create_table( $wpdb->get_blog_prefix() ); 
} 
  1. 插入以下代码以提供ch8bt_create_table函数的实现,该函数负责实际创建表:
function ch8bt_create_table( $prefix ) { 
    // Prepare SQL query to create database table 
    // using function parameter 

    $creation_query = 'CREATE TABLE IF NOT EXISTS ' . 
                      $prefix . 'ch8_bug_data ( 
                      `bug_id` int(20) NOT NULL AUTO_INCREMENT, 
                      `bug_description` text, 
                      `bug_version` varchar(10) DEFAULT NULL, 
                      `bug_report_date` date DEFAULT NULL, 
                      `bug_status` int(3) NOT NULL DEFAULT 0, 
                      PRIMARY KEY (`bug_id`) 
                      );'; 

    global $wpdb; 
    $wpdb->query( $creation_query ); 
} 
  1. 保存并关闭插件文件。

  2. 导航到插件管理页面,并激活“第八章 - Bug Tracker”插件。

  3. 使用phpMyAdmin连接到您的 MySQL 数据库,以查看在插件激活时是否创建了一个新表:

图片

它是如何工作的...

与我们在第三章“创建默认用户设置”菜谱中提到的创建配置选项类似,当在 WordPress 安装中激活插件时,通常会创建自定义数据库表。通过使用激活钩子,我们注册代码以便在插件首次激活和升级时执行。当回调函数执行时,我们将首次接触到全局的wpdb类。这个实用类由 WordPress 实例化,并为我们提供了访问一系列方法,这些方法可以用来与底层的 MySQL 网站数据库进行交互,同时也有助于防止数据相关的安全风险。这些方法在复杂度上有所不同,从快速插入或更新记录的简单调用到需要了解 SQL 命令以产生预期结果的更复杂成员函数。

在调用创建实际表之前,激活函数调用wpdb类的get_blog_prefix方法来检索与网站关联的表前缀(在默认安装中设置为wp_)。在检索后,这个前缀立即发送到ch8bt_create_table函数,以构建一个用于创建新表的 SQL 命令。

虽然 SQL 命令有多个行,但如果我们将其分解成小部分,我们会发现它实际上相当简单。命令的第一行指定如果服务器上尚未存在,则应创建一个名为<prefix>ch8_bug_data的新表。如果创建发生,接下来的五行指定每个字段的名称和数据类型,以及指示字段是否可以包含NULL值以及在某些情况下默认值应该是什么的信息。还有一个与bug_id字段相关的特殊命令,称为AUTO_INCREMENT命令,它告诉系统在向表中添加新记录时自动用自增值填充此字段。最后,但同样重要的是,代码的最后一行指示表的键是bug_id字段。

一旦查询准备就绪,它就会被存储在一个变量中,并通过调用wpdb对象的query方法来执行。此方法在网站数据库上执行任何 SQL 命令,并返回一个数值,表示查询影响了多少行。

还有更多...

虽然之前的代码相对容易管理,但在处理大量字段或网络 WordPress 安装时,事情可能会变得稍微复杂一些。

使用 phpMyAdmin 简化代码创建

而不是从头开始编写表创建代码,phpMyAdmin数据库管理工具可以派上用场来准备此代码:

图片

例如,要创建本食谱中使用的表,请按照以下步骤操作:

  1. phpMyAdmin中选择wordpressdev数据库。

  2. 在“创建表”部分,在名称字段中输入wpdev_ch8_bug_data,在字段数量中输入数字5

  3. 点击“Go”按钮。

  4. 在显示的表创建网格中,根据上一张截图中的列名设置每个字段的名称。

  5. 根据上一张截图中的类型列设置每个字段的类型。

  6. 对于在其类型旁边有括号的值,使用数值来表示这些项的长度/值。

  7. 根据上一张截图设置每个字段的默认值。对于具有NULL默认值的项,可以从下拉列表中选择 NULL。对于具有特定值的项,在下拉列表中选择“按定义:”,并在相邻字段中指示该值。

  8. 对于允许有NULL值的项(在上一张截图中以“是”显示),请确保已勾选“Null”框。

  9. 在“索引”下拉列表中选择bug_id字段下的“PRIMARY”,以表示它将是表的键。然后,点击出现的索引创建弹出对话框中的“Go”。

  10. 对于 bug_id 字段,勾选 A_I 框以指示在表中插入新值时应自动递增。

  11. 点击“保存”按钮以完成表创建过程。

在此时刻,phpMyAdmin 将在服务器上创建表。要访问创建表的 SQL 命令,请点击导出标签,然后点击转到下载包含 SQL 代码的文本文件。虽然导出文件将包含一些额外信息,但重要部分是实际的 CREATE TABLE 部分,正如你将看到的,它看起来与这个配方中编写的代码非常相似。

在网络安装中创建表

WordPress 的许多优点之一是能够从单个安装中创建和管理多个网站。在这些情况下,每个网站在 MySQL 数据库中都有自己的表集。因此,当准备一个可能用于网络安装的创建自定义表的插件时,必须在每个网站的架构下放置额外的代码来创建新表。

首先在ch8bt_activation函数中进行更改,检查我们是否正在处理多站点安装。如果是这种情况,我们将遍历每个现有网站,并调用创建新表,就像我们在主要配方代码中所看到的那样:

function ch8bt_activation() { 
    // Get access to global database access class 
    global $wpdb; 

    // Check to see if WordPress installation is a network 
    if ( is_multisite() ) { 
        // If it is, cycle through all blogs, switch to them 
        // and call function to create plugin table 
        if ( !empty( $_GET['networkwide'] ) ) { 
            $start_blog = $wpdb->blogid; 
            $blog_list =
                $wpdb->get_col( 'SELECT blog_id FROM ' . $wpdb->blogs );
            foreach ( $blog_list as $blog ) {
                switch_to_blog( $blog ); 
                // Send blog table prefix to creation function
                ch8bt_create_table( $wpdb->get_blog_prefix() );
            } 
            switch_to_blog( $start_blog );
            return;
        }
    }

    // Create table on main blog in network mode or single blog
    ch8bt_create_table( $wpdb->get_blog_prefix() );
} 

虽然这将处理在插件激活时在所有现有网络网站上创建自定义表,但还需要放置额外的代码来在创建新网站时创建额外的表:

// Register function to be called when new blogs are added
// to a network site 
add_action( 'wpmu_new_blog', 'ch8bt_new_network_site' );

function ch8bt_new_network_site( $blog_id ) {  
    global $wpdb; 

    // Check if this plugin is active when new blog is created
    // Include plugin functions if it is
    if ( !function_exists( 'is_plugin_active_for_network' ) ) {
        require_once( ABSPATH . '/wp-admin/includes/plugin.php' ); 
    }

    // Select current blog, create new table and switch back
    if ( is_plugin_active_for_network( plugin_basename( __FILE__ ) ) ) {
        $start_blog = $wpdb->blogid; 
        switch_to_blog( $blog_id ); 

        // Send blog table prefix to table creation function 
        ch8bt_create_table( $wpdb->get_blog_prefix() ); 
        switch_to_blog( $start_blog ); 
    } 
} 

ch8bt_create_table函数本身不需要任何修改,因为它已经被设计为从其他函数接收表前缀并使用它来构建查询。

在插件移除时删除自定义表

对于插件来说,提供一个卸载程序以删除它们添加到网站数据库或文件系统中的内容,始终是一个好的做法。当处理自定义数据库表时,当网站管理员决定删除插件时,所有记录都应该与表本身一起删除。

这个配方展示了如何实现一个数据删除脚本,以删除在先前的配方中创建的错误存储表。

准备工作

你应该已经遵循了创建新的数据库表的步骤,以便有一个现有的表可以删除。或者,你可以从代码包中获取结果代码(第八章/ch8-bug-tracker/ch8-bug-tracker-v1-1.php),并将文件重命名为ch8-bug-tracker.php

如何做到这一点...

  1. 导航到你的开发安装的 WordPress 插件目录。

  2. ch8-bug-tracker目录中创建一个名为uninstall.php的文本文件,并在代码编辑器中打开它。

  3. 以标准的<?php打开标签开始新的脚本。

  4. 通过向文件中添加以下代码来实现一个名为ch8bt_drop_table的新函数:

function ch8bt_drop_table( $prefix ) { 
    global $wpdb;     
    $wpdb->query( 'DROP TABLE ' . $prefix . 'ch8_bug_data' );     
} 
  1. 添加以下代码以执行从单个或网络 WordPress 安装中存储错误创建的表的删除:
// Check that file was called from WordPress admin 
if( !defined( 'WP_UNINSTALL_PLUGIN' ) ) {
    exit();
}

global $wpdb; 

// Check if site is configured for network installation 
if ( is_multisite() ) { 
    if ( !empty( $_GET['networkwide'] ) ) { 
        // Get blog list and cycle through all blogs 
        $start_blog = $wpdb->blogid; 
        $blog_list = $wpdb->get_col( 'SELECT blog_id FROM ' .
                                     $wpdb->blogs ); 
        foreach ( $blog_list as $blog ) { 
            switch_to_blog( $blog ); 
            // Call function to delete bug table with prefix 
            ch8bt_drop_table( $wpdb->get_blog_prefix() ); 
        } 
        switch_to_blog( $start_blog ); 
        return; 
    }     
}  

ch8bt_drop_table( $wpdb->prefix ); 
  1. 保存并关闭代码文件。

  2. 导航到插件管理页面并停用第八章 - Bug Tracker 插件。

  3. 在执行下一步之前,请复制整个插件目录,以避免删除所有工作。

  4. 点击插件上的删除链接,然后在确认删除插件及其数据的对话框中点击确定。

  5. 使用phpMyAdmin连接到你的 MySQL 数据库以验证错误数据表已被删除。

它是如何工作的...

正如我们在第二章中看到的,插件框架基础,一个名为uninstall.php的文件中包含的所有代码在删除插件时都会被执行。在这种情况下,我们代码的主要目的是对网站数据库运行查询以删除错误表。

在这样做之前,文件的最初几行会检查一个变量(WP_UNINSTALL_PLUGIN)的存在,以确认代码是作为插件删除过程的一部分被调用的,而不是由外部用户调用的。

一旦确认了执行的合法性,运行的代码类似于表创建代码,我们首先获取对 WordPress 数据库管理类的访问权限,然后检查 WordPress 安装是单个站点还是网络安装。在前一种情况下,我们调用一次ch8bt_drop_table函数来删除错误表,而在网络环境下,我们对每个现有站点多次调用该函数。

删除表的查询实际上非常简单,通过调用wpdb类的query方法来执行DROP TABLE SQL 命令。

参考以下

  • 创建新的数据库表配方

在插件升级时更新自定义表结构

在插件的生命周期中,随着它扩展以提供额外的功能,可能需要在自定义数据库表中存储比最初打算存储更多的数据。正如你所知,WordPress 本身在升级过程中会对其数据库结构进行定期更改以存储新信息。为此,它使用一个简单的函数dbDelta,我们也可以从我们的插件代码中访问它。

这个配方展示了如何修改之前的表创建代码,以加载 WordPress 升级 API 并使用数据库升级功能向现有的错误存储表添加额外字段。

准备工作

你应该已经遵循了创建新的数据库表的配方,以便修改创建代码。或者,你也可以从代码包中获取结果代码(Chapter 8/ch8-bug-tracker/ch8-bug-tracker-v1-1.php),并将文件重命名为ch8-bug-tracker.php

如何操作...

  1. 导航到你的开发安装的 WordPress 插件目录。

  2. 导航到ch8-bug-tracker目录并编辑ch8-bug-tracker.php

  3. 定位到ch8bt_create_table函数。

  4. 在表创建查询的第一行删除IF NOT EXISTS文本。

  5. 在表创建代码中添加一行额外的代码以添加一个字段来存储错误标题,如下所示(粗体):

$creation_query = 'CREATE TABLE ' . $prefix . 
                  'ch8_bug_data ( 
                  `bug_id` int(20) NOT NULL AUTO_INCREMENT, 
                  `bug_description` text, 
                  `bug_version` varchar(10) DEFAULT NULL, 
                  `bug_report_date` date DEFAULT NULL, 
                  `bug_status` int(3) NOT NULL DEFAULT 0, 
                  `bug_title` VARCHAR( 128 ) NULL, 
                  PRIMARY KEY (`bug_id`) 
                  );'; 
  1. 定位以下代码行:
global $wpdb; 
wpdb->query( $creation_query ); 

应将其替换为以下代码行:

require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); 
dbDelta( $creation_query ); 
  1. 保存并关闭插件文件。

  2. 导航到插件管理页面。

  3. 禁用并重新激活第八章 - Bug Tracker 插件。

  4. 使用 phpMyAdmin 连接到您的 MySQL 数据库,查看新添加的 bug_title 字段是否已添加到错误存储表中:

图片

它是如何工作的...

dbDelta 函数是 WordPress 在执行版本升级时调用的实用函数之一。当被调用时,它会解析它接收到的表创建 SQL 命令,并确定它所描述的表结构与当前表之间的差异(如果表存在的话)。一旦确定了这种差异,它就会执行必要的更改以使两个结构对齐。

如果两个结构完全相同,它将保持表不变。采用这种方法,任何对结构的变化都可以通过更改表创建查询简单地实现。因此,dbDelta 函数实际上可以从插件的第一版本开始使用,以确保升级路径简单易行。

参见

  • 创建新的数据库表 菜谱

在管理页面上显示自定义表数据

在创建一个或多个自定义数据库表以存储数据后,创建自定义项目管理系统下一步是构建一个界面来填充它们。虽然自定义帖子类型有一个非常有序的结构来编辑条目,但创建自定义表界面与创建插件配置面板非常相似,正如我们在第三章用户设置和管理页面中看到的。

这个菜谱展示了如何创建一个界面,该界面将显示系统中存储的错误列表,提供一个创建新条目的链接,并提供编辑现有条目的方法。

准备中

您应该已经遵循了 在插件升级时更新自定义表结构 的菜谱,以便有一个具有所需结构的自定义表。或者,您可以从代码包中获取结果代码(Chapter 8/ch8-bug-tracker/ch8-bug-tracker-v2.php),并将文件重命名为 ch8-bug-tracker.php

如何做...

  1. 导航到您的开发安装的 WordPress 插件目录。

  2. 导航到 ch8-bug-tracker 目录并编辑 ch8-bug-tracker.php

  3. 在构建管理菜单时注册要调用的函数,请插入以下代码行:

add_action( 'admin_menu', 'ch8bt_settings_menu' ); 
  1. 添加以下代码以提供 ch8bt_settings_menu 函数的实现:
function ch8bt_settings_menu() { 
    add_options_page( 'Bug Tracker Data Management',   
                      'Bug Tracker',
                      'manage_options', 'ch8bt-bug-tracker',
                      'ch8bt_config_page' ); 
} 
  1. 将以下代码块附加到为负责渲染配置页面的 ch8bt_config_page 函数提供实现的代码:
function ch8bt_config_page() { 
    global $wpdb;  
    ?> 

    <!-- Top-level menu --> 
    <div id="ch8bt-general" class="wrap"> 
    <h2>Bug Tracker <a class="add-new-h2" href="<?php echo 
         add_query_arg( array( 'page' => 'ch8bt-bug-tracker', 
                               'id' => 'new' ),  
                        admin_url('options-general.php') ); ?>"> 
    Add New Bug</a></h2> 

    <!-- Display bug list if no parameter sent in URL --> 
    <?php if ( empty( $_GET['id'] ) ) { 
        $bug_query = 'select * from ' . $wpdb->get_blog_prefix();
        $bug_query .= 'ch8_bug_data ORDER by bug_report_date DESC';
        $bug_items = $wpdb->get_results( $bug_query, ARRAY_A );
    ?> 

    <h3>Manage Bug Entries</h3> 

    <table class="wp-list-table widefat fixed"> 
    <thead><tr><th style="width: 80px">ID</th> 
    <th style="width: 300px">Title</th>
    <th>Version</th></tr></thead>

    <?php  
        // Display bugs if query returned results 
        if ( $bug_items ) {             
            foreach ( $bug_items as $bug_item ) { 
                echo '<tr style="background: #FFF">'; 
                echo '<td>' . $bug_item['bug_id'] . '</td>';
                echo '<td><a href="'; 
                echo add_query_arg( array(  
                    'page' => 'ch8bt-bug-tracker', 
                    'id' => $bug_item['bug_id'] ), 
                    admin_url( 'options-general.php' ) ); 
                echo '">' . $bug_item['bug_title'] . '</a></td>'; 
                echo '<td>' . $bug_item['bug_version'];
                echo '</td></tr>';                     
            } 
        } else {
            echo '<tr style="background: #FFF">'; 
            echo '<td colspan="3">No Bug Found</td></tr>'; 
        }        
    ?> 
    </table><br /> 
    <?php } ?> 
    </div> 
<?php } 
  1. 保存并关闭插件文件。

  2. 导航到管理页面设置菜单下的新 Bug Tracker 项目,以查看新创建的页面,显示系统中目前没有存储任何错误:

图片

它是如何工作的...

菜谱的前几个步骤使用了在第三章(0346c3c6-27ee-45fb-bfd6-df398e04b2b4.xhtml),用户设置和管理页面中之前介绍过的函数,来注册一个回调函数,该回调函数将添加到管理菜单的设置部分。当访问新的菜单页面时,ch8bt_config_page 函数会被调用以渲染页面内容,使用 HTML 和 PHP 代码的混合。

在渲染页面标题以及用于创建新错误的链接之后,页面显示代码会检查页面地址是否包含一个名为 id 的变量。这个 ID 将在后续的菜谱中用来指示用户是想创建还是编辑错误。当访客点击错误跟踪器菜单项时,不会设置此 ID,从而导致当前菜谱代码被调用。

下一个部分使用 wpdb 数据库管理类的 get_results 方法从数据库中检索信息。在这个调用中,第一个参数是一个 SQL 查询,而第二个参数指示返回数据所使用的期望格式。虽然我们指定了在这种情况下我们想要一个关联数组,但其他选项包括返回一个数字索引数组(ARRAY_N)、一个对象(OBJECT)或一个对象数组(OBJECT_K)。

查询中的 SELECT * 命令表示我们想要返回表中的所有字段,而 ORDER 命令指定了用于排序结果的字段以及排序方向(ASCDESC)。

一旦执行了 get_results 方法,我们将检查是否从数据库中检索到了任何数据,如果找到数据,我们将通过一个 foreach 循环遍历所有记录,并在标准 HTML 表格中显示它们。如果查询没有返回任何记录,我们将显示一条简短的消息,表明没有找到错误。

相关内容

  • 在第三章,用户设置和管理页面中的在设置菜单中创建管理页面菜单项菜谱

  • 在第三章,用户设置和管理页面中的使用 HTML 渲染管理页面内容菜谱

在自定义表中插入和更新记录

现在我们已经建立了一个基本的基础设施来显示现有的错误,下一步的逻辑步骤是创建一个表单,该表单将被用来在自定义表中插入和更新记录。

这个菜谱展示了当用户在错误跟踪列表中选择一个条目或通过适当的链接表示他们想要创建一个新条目时,如何添加一个表单来管理错误。

准备工作

你应该已经遵循了在管理页面中显示自定义表数据菜谱,以建立一个现有的框架。或者,你也可以从代码包中获取结果代码(Chapter 8/ch8-bug-tracker/ch8-bug-tracker-v3.php)并将文件重命名为 ch8-bug-tracker.php

如何操作...

  1. 导航到你的开发安装的 WordPress 插件目录。

  2. 导航到ch8-bug-tracker目录并编辑ch8-bug-tracker.php

  3. 找到ch8bt_config_page函数并定位其主体末尾关闭if语句的括号(<?php } ?>)。

  4. 在上一步中识别出的if语句的关闭括号之前插入以下代码块:

<?php } elseif ( isset( $_GET['id'] ) &&
                 ( 'new' == $_GET['id'] ||  
                   is_numeric( $_GET['id'] ) ) ) {                  
    $bug_id = intval( $_GET['id'] ); 
    $mode = 'new'; 

    // Query database if numeric id is present 
    if ( $bug_id > 0 ) { 
        $bug_query = 'select * from ' . $wpdb->get_blog_prefix(); 
        $bug_query .= 'ch8_bug_data where bug_id = %d'; 

        $bug_data =
            $wpdb->get_row( $wpdb->prepare( $bug_query, $bug_id ), 
                            ARRAY_A ); 

        // Set variable to indicate page mode 
        if ( $bug_data ) {
            $mode = 'edit'; 
        }
    } 

    if ( 'new' == $mode ) {
        $bug_data = array(
            'bug_title' => '', 'bug_description' => '',
            'bug_version' => '', 'bug_status' => ''
        ); 
    }

    // Display title based on current mode 
    if ( 'new' == $mode ) { 
        echo '<h3>Add New Bug</h3>'; 
    } elseif ( 'edit' == $mode ) { 
        echo '<h3>Edit Bug #' . $bug_data['bug_id'] . ' - '; 
        echo $bug_data['bug_title'] . '</h3>'; 
    } 
    ?> 

    <form method="post"  
          action="<?php echo admin_url( 'admin-post.php' ); ?>"> 
    <input type="hidden" name="action" value="save_ch8bt_bug" /> 
    <input type="hidden" name="bug_id" 
           value="<?php echo $bug_id; ?>" /> 

    <!-- Adding security through hidden referrer field --> 
    <?php wp_nonce_field( 'ch8bt_add_edit' ); ?> 

    <!-- Display bug editing form --> 
    <table> 
        <tr> 
            <td style="width: 150px">Title</td> 
            <td><input type="text" name="bug_title" size="60"
                       value="<?php echo esc_html( 
                       $bug_data['bug_title'] ); ?>"/></td> 
        </tr> 
        <tr> 
            <td>Description</td> 
            <td><textarea name="bug_description"  
            cols="60"><?php echo
esc_textarea( $bug_data['bug_description'] ); ?></textarea></td> 
        </tr> 
        <tr> 
            <td>Version</td> 
            <td><input type="text" name="bug_version" 
                       value="<?php echo esc_html( 
                       $bug_data['bug_version'] ); ?>" /></td> 
        </tr> 
        <tr> 
            <td>Status</td> 
            <td> 
                <select name="bug_status"> 
                <?php  
                // Display drop-down list of bug statuses
                $bug_statuses = array( 0 => 'Open', 1 => 'Closed',
                                       2 => 'Not-a-Bug' );
                foreach( $bug_statuses as $status_id => $status ) { 
                    // Add selected tag when entry matches 
                    echo '<option value="' . $status_id . '" '; 
                    selected( $bug_data['bug_status'], 
                              $status_id ); 
                    echo '>' . $status; 
                } 
                ?> 
                </select> 
            </td> 
        </tr> 
    </table> 
    <input type="submit" value="Submit" class="button-primary" /> 
</form> 
  1. 添加以下代码行以注册一个在初始化管理页面时被调用的函数:
add_action( 'admin_init', 'ch8bt_admin_init' ); 
  1. 在插件文件末尾添加以下代码块以注册一个在创建或更新错误时被调用的函数:
function ch8bt_admin_init() { 
    add_action( 'admin_post_save_ch8bt_bug', 'process_ch8bt_bug' ); 
} 
  1. 将以下代码块附加以处理用户提交的数据并将其存储在网站数据库中:
function process_ch8bt_bug() {  
    if ( !current_user_can( 'manage_options' ) ) {
        wp_die( 'Not allowed' );
    }

    // Check if nonce field is present for security 
    check_admin_referer( 'ch8bt_add_edit' ); 
    global $wpdb;

    // Place all user submitted values in an array (or empty 
    // strings if no value was sent) 
    $bug_data = array(); 
    $bug_data['bug_title'] = ( isset( $_POST['bug_title'] ) ?
        sanitize_text_field( $_POST['bug_title'] ) : '' ); 

    $bug_data['bug_description'] =
        ( isset( $_POST['bug_description'] ) ? 
          sanitize_text_field( $_POST['bug_description'] ) : '' ); 

    $bug_data['bug_version'] = ( isset( $_POST['bug_version'] ) ? 
        sanitize_text_field( $_POST['bug_version'] ) : '' ); 

    // Set bug report date as current date 
    $bug_data['bug_report_date'] = date( 'Y-m-d' ); 

    // Set status of all new bugs to 0 (Open) 
    $bug_data['bug_status'] = ( isset( $_POST['bug_status'] ) ? 
        intval( $_POST['bug_status'] ) : 0 ); 

    // Call the wpdb insert or update method based on value 
    // of hidden bug_id field 
    if ( isset( $_POST['bug_id'] ) && 0 == $_POST['bug_id'] ) { 
        $wpdb->insert( $wpdb->get_blog_prefix() . 'ch8_bug_data', 
                       $bug_data ); 
    } elseif ( isset( $_POST['bug_id'] ) && 
               $_POST['bug_id'] > 0 ) { 
        $wpdb->update( $wpdb->get_blog_prefix() . 'ch8_bug_data',  
            $bug_data, 
            array( 'bug_id' => intval( $_POST['bug_id'] ) ) ); 
    } 

    // Redirect the page to the user submission form
    wp_redirect( add_query_arg( 'page', 'ch8bt-bug-tracker', 
                     admin_url( 'options-general.php' ) ) ); 
    exit; 
} 
  1. 保存并关闭插件文件。

  2. 导航到管理页面设置菜单下的新错误跟踪器项目,并点击“添加新错误”链接以创建条目:

图片

  1. 点击提交以将新错误存储在网站数据库中。新创建的错误将出现在上一步中创建的错误列表中。

  2. 点击新条目的名称以查看其信息和更新它。

它是如何工作的...

如果你尝试点击上一步中创建的“添加新错误”链接,你会看到一个只包含面板标题的页面。这是由于我们没有实现当网站地址中存在id变量时显示错误创建和编辑表单的代码。

本食谱的前几个步骤旨在通过检查页面 URL 中是否存在名为id的变量,其值设置为文本new或数值,来纠正这个问题。

虽然这两种情况都会显示错误编辑表单,但第二种情况首先使用wpdb对象的get_row方法执行数据库查询,尝试检索具有指定 ID 的错误。get_row方法类似于之前食谱中使用的get_results方法,但即使查询找到多个结果,也只会返回单行。作为我们的get_row调用的一部分,我们还使用了$wpdb类的 prepare 方法。此方法将解析它接收的第二个参数以进行安全性检查,然后使用它来替换查询中的占位符。如果查询成功,检索到的值将用于自定义表单标题并设置初始字段值。

表单本身是一个标准的 HTML 表单,包括我们在之前的食谱中看到的一些元素,例如调用wp_nonce_field以提供外部攻击的安全性。我们还添加了一个包含在页面 URL 中找到的错误 ID 的隐藏字段,以便在提交错误时方便数据处理。

一旦表单就位,我们调用add_action来注册一个回调,该回调将在新创建的表单提交时执行。

回调函数名为process_ch8bt_bug,首先进行一些验证。具体来说,它检查当前用户是否有管理权限,以及是否在表单数据中存在应该包含的 nonce 字段。如果这两个条件都满足,则从用户帖子数据、当前系统日期和硬编码的状态值创建一个数据数组。

结果数组使用两种wpdb对象方法之一存储在网站数据库中,即insertupdate,这取决于隐藏的bug_id字段中找到的值。这两种方法都期望接收目标表名,以及包含每个要存储的表字段名称和值的关联数组。此外,update方法需要一个第三个参数,该参数指示用于定位要更新的字段的字段名称和值。在两种情况下,您都会注意到bug_id字段没有指定在新值数组中,因为它由数据库服务器自动设置为增量值。

此函数的最后一个步骤是构建一个指向插件配置页面的干净 URL,并在wp_redirect调用中使用该地址。

参见

  • 在管理页面中显示自定义表数据的配方

从自定义表中删除记录

在向自定义表中添加数据后,网站管理员可能会在将来删除这些条目中的一些。由于我们一直在构建一个用于查看、创建和修改数据库条目的界面,因此选择要删除的项目的工作也落在了我们的责任之下。幸运的是,我们可以轻松地扩展现有的错误显示列表,添加用于选择的复选框和用于触发实际删除的按钮。

这个配方展示了如何为我们的错误跟踪系统添加删除功能。

准备工作

您应该已经遵循了在自定义表中插入和更新记录的配方,以有一个现有的框架来增强。或者,您可以从代码包中获取结果代码(第八章/ch8-bug-tracker/ch8-bug-tracker-v4.php),并将文件重命名为ch8-bug-tracker.php

如何做到这一点...

  1. 导航到您的开发安装的 WordPress 插件目录。

  2. 导航到ch8-bug-tracker目录并编辑ch8-bug-tracker.php

  3. 找到ch8bt_config_page函数,并定位其内容中的管理错误条目 h3标题。

  4. 在标题之后立即插入以下突出显示的代码行以创建一个表单:

<h3>Manage Bug Entries</h3> 

<form method="post"
      action="<?php echo admin_url( 'admin-post.php' ); ?>"> 
<input type="hidden" name="action" value="delete_ch8bt_bug" />  
<!-- Adding security through hidden referrer field --> 
<?php wp_nonce_field( 'ch8bt_deletion' ); ?> 
  1. 在下面几行中,在ID字段之前,在表头中添加一个空列,如以下代码行所示:
<thead><tr><th style="width: 50px"></th>
       <th style='width: 80px'>ID</th>
  1. 在主错误列表显示循环中,插入以下突出显示的代码段,在每个项目前添加一个复选框:
echo '<tr style="background: #FFF">'; 
echo '<td><input type="checkbox" name="bugs[]" value="'; 
echo intval( $bug_item['bug_id'] ) . '" /></td>'; 
echo '<td>' . $bug_item['bug_id'] . '</td>';
  1. 在下面几行中,将colspan表格行参数的值从3更改为4
echo '<td colspan="4">No Bug Found</td></tr>'; 
  1. table关闭标签之后附加以下突出显示的代码行,以显示删除按钮并结束表单部分:
</table><br /> 

<input type="submit" value="Delete Selected" 
 class="button-primary"/> 
</form> 
  1. 找到ch8bt_admin_init函数,并在其体末尾添加以下函数调用:
add_action( 'admin_post_delete_ch8bt_bug', 'delete_ch8bt_bug' ); 
  1. 导航到文件底部,并添加以下代码块,以提供处理由新表单生成的删除请求的delete_ch8bt_bug函数的实现:
function delete_ch8bt_bug() { 
    // Check that user has proper security level 
    if ( !current_user_can( 'manage_options' ) ) {
        wp_die( 'Not allowed' );
    }

    // Check if nonce field is present 
    check_admin_referer( 'ch8bt_deletion' );

    // If bugs are present, cycle through array and call SQL 
    // command to delete entries one by one
    if ( !empty( $_POST['bugs'] ) ) { 
        // Retrieve array of bugs IDs to be deleted 
        $bugs_to_delete = $_POST['bugs'];         
        global $wpdb;

        foreach ( $bugs_to_delete as $bug_to_delete ) { 
            $query = 'DELETE from ' . $wpdb->get_blog_prefix(); 
            $query .= 'ch8_bug_data WHERE bug_id = %d'; 
            $wpdb->query( $wpdb->prepare( $query,
                          intval( $bug_to_delete ) ) ); 
        }         
    }

    // Redirect the page to the user submission form 
    wp_redirect( add_query_arg( 'page', 'ch8bt-bug-tracker', 
                 admin_url( 'options-general.php' ) ) ); 
    exit;   
} 
  1. 保存并关闭插件文件。

  2. 导航到管理页面设置菜单下的新 Bug Tracker 项,以查看添加到 bug 列表中的新界面元素。

它是如何工作的...

虽然从我们的自定义表中实际删除数据可以通过运行DELETE SQL 命令的单个调用完成,但我们首先需要用户指出哪些条目需要被删除。这个选择界面可以很容易地添加到之前菜谱中创建的现有 bug 列表。

这个菜谱从创建一个标准的 HTML 表单开始,用于包围原始的 bug 列表。除了 bug 列表外,表单还包括一个隐藏字段,用于指示当用户提交表单时要调用的操作名称,以及一个 nonce 字段以确保删除过程的访问安全。

在此初始代码的基础上,菜谱的下一部分修改了原始表列表,为每一行前面添加一个复选框。从代码中可以看出,复选框的name属性与常规 HTML 语法略有不同,以两个方括号结尾。这种语法与每个项目的bug_id结合使用,结果是在提交时创建一个已选项目数组和 ID 数字数组,这些数组被发送到表单处理函数。

在 bug 列表显示代码中进行的最后一个更改是添加一个删除按钮并关闭表单。

为了将回调与新建的表单关联,菜谱接下来的添加是一个对add_action的调用,将admin_post_<actionname>变量操作名称与delete_ch8bt_bug函数关联。

当被调用时,bug 删除函数,就像我们之前创建的大多数其他提交处理代码一样,首先进行一些验证,以确保用户具有适当的权限,并且表单中放置的隐藏安全字段存在。当这两项正式程序都得到确认后,代码将继续检查 bug 数组的存在,并在找到的情况下遍历所有条目。在那个循环中,我们获得对全局wpdb类的访问权限,并可以使用它构建和执行 SQL 查询,每次使用提交的bug_id数字删除单个数据库行。

作为额外的安全措施,请注意在$bug_to_delete变量前面使用intval函数,以确保没有人试图处理外部命令,试图破坏或劫持数据库。

参见

  • 在自定义表中插入和更新记录的菜谱

在短代码中显示自定义数据库表数据

创建自定义表的目的通常是存储与网站访客共享的信息。因此,为用户提供在他们的网站上轻松显示存储在自定义表中的新内容的能力是很重要的。实现这一目标的最直接方法是创建一个或多个短代码,可以在任何帖子或页面上插入以呈现所需的信息。

本食谱展示了如何实现一个新的短代码,该短代码将用于在页面上显示虫子列表。

准备工作

您应该已经遵循了 从自定义表中删除记录 的食谱,以便有一个现有的框架来增强。或者,您可以从代码包中获取结果代码(第八章/ch8-bug-tracker/ch8-bug-tracker-v5.php)并将文件重命名为 ch8-bug-tracker.php

如何实现...

  1. 导航到您的开发安装的 WordPress 插件目录。

  2. 导航到 ch8-bug-tracker 目录并编辑 ch8-bug-tracker.php

  3. 在文件底部添加以下代码行以声明一个新的短代码及其关联的显示函数:

add_shortcode( 'bug-tracker-list', 'ch8bt_shortcode_list' ); 
  1. 在部分标题之后立即插入以下代码块以实现负责显示虫子列表的ch8bt_shortcode_list函数:
function ch8bt_shortcode_list() { 
    global $wpdb;

    // Prepare query to retrieve bugs from database 
    $bug_query = 'select * from ' . $wpdb->get_blog_prefix();
    $bug_query .= 'ch8_bug_data ';
    $bug_query .= 'ORDER by bug_id DESC';                
    $bug_items = $wpdb->get_results( $bug_query, ARRAY_A );

    // Prepare output to be returned to replace shortcode
    $output = '';
    $output .= '<div class="bug-tracker-list"><table>';

    // Check if any bugs were found 
    if ( !empty( $bug_items ) ) { 
        $output .= '<tr><th style="width: 80px">ID</th>'; 
        $output .= '<th style="width: 300px">Title / Desc</th>'; 
        $output .= '<th>Version</th></tr>';

        // Create row in table for each bug 
        foreach ( $bug_items as $bug_item ) { 
            $output .= '<tr style="background: #FFF">'; 
            $output .= '<td>' . $bug_item['bug_id'] . '</td>'; 
            $output .= '<td>' . $bug_item['bug_title'] . '</td>'; 
            $output .= '<td>' . $bug_item['bug_version'] . '</td>'; 
            $output .= '</tr><tr><td></td><td colspan="2">'; 
            $output .= $bug_item['bug_description']; 
            $output .= '</td></tr>'; 
        } 
    } else { 
        // Message displayed if no bugs are found 
        $output .= '<tr style="background: #FFF">'; 
        $output .= '<td colspan="3">No Bugs to Display</td>'; 
    }             
    $output .= '</table></div>';

    // Return data prepared to replace shortcode on page/post 
    return $output; 
} 
  1. 保存并关闭插件文件。

  2. 创建一个新页面,并在页面正文中插入新创建的短代码 [bug-tracker-list]

  3. 查看页面以查看存储在系统中的虫子列表:

图片

工作原理...

以与之前食谱非常相似的方式创建用于显示自定义表数据的新的短代码。首先,我们声明新的代码,以及当在帖子或页面中找到时将被调用的函数名称来生成文本替换它。然后,我们创建一个显示函数来准备所有输出并将其返回给 WordPress。

这里的唯一区别在于我们查询信息的方式。本食谱使用 wpdb 类的 get_results 方法,通过 SELECT SQL 命令查询存在于自定义数据库表中的所有虫子。在此调用执行后,所有找到的项目都返回一个关联数组,可以很容易地使用 foreach 循环以表格形式显示。

如果没有找到条目,本食谱将显示一条简单消息通知访客。

相关内容

  • 从自定义表中删除记录 的食谱

实现搜索功能以检索自定义表数据

虽然使用自定义帖子类型创建的内容可以自动由内置的 WordPress 搜索引擎搜索,但自定义数据库表则没有同样的待遇。相反,选择此机制来存储信息的插件开发者必须构建自己的搜索功能。

本食谱展示了如何在上一节创建的虫子列表中添加搜索框,以及如何使用生成的查询数据来缩小显示的虫子列表。

准备工作

您应该已经遵循了名为在短代码中显示自定义数据库表数据的配方,以便有一个现有的框架进行增强。或者,您可以从代码包中获取结果代码(第八章/ch8-bug-tracker/ch8-bug-tracker-v6.php),并将文件重命名为ch8-bug-tracker.php

如何操作...

  1. 导航到您的开发安装的 WordPress 插件目录。

  2. 导航到ch8-bug-tracker目录并编辑ch8-bug-tracker.php

  3. 找到ch8bt_shortcode_list函数,并在初始global $wpdb调用之后添加以下突出显示的代码,以检查是否有访客输入了搜索字符串:

global $wpdb; 

if ( !empty( $_GET['searchbt'] ) ) { 
 $search_string = sanitize_text_field( $_GET['searchbt'] ); 
 $search_mode = true; 
} else { 
 $search_string = "Search..."; 
 $search_mode = false; 
} 
  1. 如果有用户搜索文本,在现有查询字符串的中间插入以下突出显示的代码行以添加where参数:
$bug_query = 'select * from ' . $wpdb->get_blog_prefix(); 
$bug_query .= 'ch8_bug_data ';

// Add search string in query if present 
if ( $search_mode ) { 
 $search_term = '%'. $search_string . '%'; 
 $bug_query .= "where bug_title like '%s' "; 
 $bug_query .= "or bug_description like '%s' "; 
} else { 
 $search_term = ''; 
} $bug_query .= 'ORDER by bug_id DESC'; 
  1. 定位以下代码行:
$bug_items = $wpdb->get_results( $bug_query, ARRAY_A );

将其替换为以下代码:

if ( $search_mode ) {
    $bug_items = $wpdb->get_results( $wpdb->prepare(  
                     $bug_query, $search_term, $search_term ), 
                     ARRAY_A );
} else {
    $bug_items = $wpdb->get_results( $bug_query, ARRAY_A );
}
  1. 在表格开始渲染之前添加以下代码块,以显示简单的搜索表单:
$output = '';

$output .= '<div class="ch8_bt_search">';
$output .= '<form method="get" id="ch8_bt_search">'; 
$output .= '<div>Search bugs '; 
$output .= '<input type="text" onfocus="this.value=\'\'" '; 
$output .= 'value="' . esc_html( $search_string ) . '" '; 
$output .= 'name="searchbt" />'; 
$output .= '<input type="submit" value="Search" />'; 
$output .= '</div>'; 
$output .= '</form></div>'; 

$output .= '<div class="bug-tracker-list"><table>'; 
  1. 保存并关闭插件文件。

  2. 访问之前创建的 bug 显示页面,查看新的搜索表单。输入搜索字符串并点击搜索按钮,查看结果列表:

图片

它是如何工作的...

此配方通过显示简短表单并使用标准的 HTML GET方法捕获用户搜索字符串来实现一个简单的搜索引擎。如果页面地址中找到搜索字符串,我们将修改现有的 bug 检索查询,通过添加一个where子句来查找搜索字符串在bug_titlebug_description字段中的任何位置。

虽然直接在查询中插入搜索字符串并执行它可能看起来很自然,但我们使用wpdb类的prepare方法来组装查询并验证搜索字符串以避免恶意意图。这种方法与标准的 PHP sprintf函数非常相似,使用占位符来表示变量应该被替换的位置。

短代码显示函数的其余部分保持不变,显示的列表长度根据搜索字符串的存在和匹配查询的条目数量而变化。

参见

  • 在短代码中显示自定义数据库表数据配方

从用户文件导入自定义表中的数据

为了避免长时间的数据输入会话,对系统的一个很好的补充,例如我们在本章中设置的 Bug Tracker,就是提供用户一次性从外部文件导入大量条目的能力。为了完成这个任务,逗号分隔值(CSV)文件格式非常方便,因为它可以被大多数电子表格编辑器编辑,并且可以使用标准的 PHP 函数调用读取。

此配方在我们的 bug 跟踪系统中实现了基于 CSV 的导入功能。

准备工作

您应该已经遵循了实现一个搜索函数以检索自定义表数据配方,以有一个现有的框架进行增强。或者,您可以从代码包中获取结果代码(第八章/ch8-bug-tracker/ch8-bug-tracker-v7.php)并将文件重命名为ch8-bug-tracker.php

如何操作...

  1. 导航到您的开发安装的 WordPress 插件目录。

  2. 导航到ch8-bug-tracker目录并编辑ch8-bug-tracker.php

  3. 找到ch8bt_config_page函数,并在现有删除表单之后,在错误列表部分的末尾添加以下高亮代码块:

<input type="submit" value="Delete Selected"
       class="button-primary"/> 
</form>

<!-- Form to upload new bugs in csv format --> 
<form method="post" 
 action="<?php echo admin_url( 'admin-post.php' ); ?>" 
 enctype="multipart/form-data">  <input type="hidden" name="action" value="import_ch8bt_bug" />   
<!-- Adding security through hidden referrer field --> 
<?php wp_nonce_field( 'ch8bt_import' ); ?> <h3>Import Bugs</h3> 
<div class="import_data">Import Bugs from CSV File 
 (<a href="<?php echo plugins_url( 'importtemplate.csv', 
                                      __FILE__ ); ?>">Template</a>) 
 <input name="import_bugs_file" type="file" /></div> 
<input type="submit" value="Import" class="button-primary"/> 
</form> 
  1. 定位到ch8bt_admin_init函数,并在其主体末尾添加以下代码行以注册处理错误导入表单提交的函数:
add_action( 'admin_post_import_ch8bt_bug', 'import_ch8bt_bug' );
  1. 插入以下代码块以提供import_ch8bt_bug函数的实现:
function import_ch8bt_bug() { 
    // Check that user has proper security level 
    if ( !current_user_can( 'manage_options' ) ) {
        wp_die( 'Not allowed' );
    }

    // Check if nonce field is present 
    check_admin_referer( 'ch8bt_import' );

    // Check if file has been uploaded 
    if( array_key_exists( 'import_bugs_file', $_FILES ) ) { 
        // If file exists, open it in read mode 
        $handle =
            fopen( $_FILES['import_bugs_file']['tmp_name'], 'r' );

        // If file is successfully open, extract a row of data 
        // based on comma separator, and store in $data array 
        if ( $handle ) { 
            while ( FALSE !== 
                    ( $data = fgetcsv( $handle, 5000, ',' ) ) ) { 
                $row += 1;

                // If row count is ok and row is not header row 
                // Create array and insert in database 
                if ( count( $data ) == 4 && $row != 1 ) { 
                    $new_bug = array( 
                        'bug_title' => $data[0], 
                        'bug_description' => $data[1], 
                        'bug_version' => $data[2], 
                        'bug_status' => $data[3], 
                        'bug_report_date' => date( 'Y-m-d' ) );

                    global $wpdb;         
                    $wpdb->insert( $wpdb->get_blog_prefix() . 
                                   'ch8_bug_data', $new_bug ); 
                } 
            } 
        } 
    }

    // Redirect the page to the user submission form 
    wp_redirect( add_query_arg( 'page', 'ch8bt-bug-tracker', 
                     admin_url( 'options-general.php' ) ) ); 
    exit; 
} 
  1. 保存并关闭插件文件。

  2. 在插件目录中创建一个名为importtemplate.csv的新文本文件,并在文本编辑器中打开它。

  3. 在新创建的文件中插入以下文本,以提供一个示例错误导入:

"Title","Description","Version","Status"
"Test Import Bug","This is a test import bug","1.0","0"
  1. 保存并关闭 CSV 文本文件。

  2. 导航到管理页面设置菜单下的新错误跟踪器项目,以查看新的导入错误部分。

  3. 使用文件导入对话框定位到importtemplate.csv

  4. 导入系统中的错误列表,以查看其内容添加到数据库中:

图片

工作原理...

此配方在错误跟踪器管理页面上创建了一个小型表单,该表单仅负责将一个或多个错误上传到数据库。通过编辑importtemplate.csv文件内容并在导入对话框中选择它,用户可以通过直接将数据加载到插件首次安装时创建的自定义数据库表来快速填充系统。

除了文件上传字段外,表单还包含通常的隐藏 nonce 和动作名称字段。它还具备enctype属性,允许上传文件。

当用户提交要上传的文件时,注册的回调函数首先检查提交的用户是否有适当的权限,以及 nonce 安全字段是否作为帖子数据的一部分存在。如果这两个条件都满足,则继续检查是否已正确上传文件到网络服务器,使用array_key_exists函数通过标准 PHP$_FILES全局变量进行搜索。如您所见,它搜索的文本是表单中文件上传字段的名称。

如果已上传文件,fopen 函数会打开它,并将指向该文件的指针存储在一个局部变量中。在快速验证指针存在之后,代码将移动到一个 while 循环中,使用 fgetcsv 函数处理传入文件的每一行。此函数一次读取文件的一行,分析其内容以找到所有存在的逗号分隔字段,并将结果数据存储在一个数字数组中。

import 函数的其余部分创建一个包含导入数据的数组,并使用 wpdb 类的 insert 方法将其存储在数据库中,正如我们在之前的菜谱中看到的。

参见

  • 在自定义表中插入和更新记录 的菜谱

第九章:利用 JavaScript、jQuery 和 AJAX 脚本

本章通过探讨以下主题来专注于在插件中集成 JavaScript:

  • 在 WordPress 网页上安全加载 jQuery

  • 使用内置的 ThickBox 插件显示弹出对话框

  • 使用短代码控制弹出对话框显示

  • 使用 Datepicker 插件显示日历日选择器

  • 使用 TipTip 插件为管理页面表单字段添加工具提示

  • 使用 AJAX 动态更新页面部分内容

简介

JavaScript 库,特别是非常流行的 jQuery 库及其众多插件,可以通过流畅的动画、动态数据查询和高级视觉功能使网站焕发生机。不幸的是,尽管它们有诸多好处,但这些脚本也可能很难处理。例如,加载多个 jQuery 副本可能会破坏其他实例所做的所有设置,并且一个脚本中的错误通常会导致其他脚本无法正确运行。

WordPress 对这种复杂架构的回答有两个方面。首先,它预包装了 jQuery 和其他许多流行的 JavaScript 库的副本,插件开发者可以使用这些库而无需加载自己的版本。然后,为了防止在页面上加载多个副本,它提供了易于使用的函数,这些函数在渲染页面之前排队脚本和样式以识别重复项。

本章展示了如何安全地加载与 WordPress 一起提供的或来自外部来源的 JavaScript 和 jQuery 文件,以便为前端页面和插件配置面板添加强大的新功能。它还解释了如何安全地运行 AJAX 查询以刷新页面部分内容。

在 WordPress 网页上安全加载 jQuery

虽然可能很诱人,但作为使用流行 JavaScript 库的新插件的一部分提供自己的 jQuery 副本,或者从 Google API 网站获取副本,但实际上 WordPress 在其安装中提供了一个 jQuery 副本,并使其加载变得非常简单。

通过使用适当的实用函数来加载 jQuery,开发者向 WordPress 请求加载此库而不是自己加载。一旦收到所有请求,它们将被分析以查找重复项,并加载每个脚本的单一实例以减少同一库多个副本之间的冲突机会。

本食谱展示了如何加载 jQuery 脚本以用于前端网站页面。

准备工作

您应该能够访问 WordPress 开发环境。

如何做到...

  1. 导航到您的开发安装的 WordPress 插件目录。

  2. 创建一个名为ch9-load-jquery的新目录。

  3. 导航到目录并创建一个名为ch9-load-jquery.php的文本文件。

  4. 在代码编辑器中打开新文件,并在插件文件顶部添加一个适当的标题,将插件命名为“第九章 - 加载 jQuery”。

  5. 添加以下代码行以注册在处理脚本加载请求时将被调用的函数:

add_action( 'wp_enqueue_scripts', 'ch9lj_front_facing_pages' ); 
  1. 将以下代码段添加到提供ch9lj_front_facing_pages函数实现的代码中:
function ch9lj_front_facing_pages() { 
    wp_enqueue_script( 'jquery' ); 
} 
  1. 保存并关闭插件文件。

  2. 前往 WordPress 管理界面中的“外观”下的“主题”菜单部分。

  3. 点击“添加新主题”并搜索名为Twenty Eleven的主题。

  4. 在你的网站上安装该主题并激活它。

  5. 访问你的网站并查看页面源代码,搜索jquery.js库的实例。除非你已激活要求加载 jQuery 的其他插件,否则你的搜索结果应该是空的。

  6. 导航到插件管理页面并激活第九章 - 加载 jQuery插件。

  7. 返回到你的网站上的任何页面并查看页面源代码。

  8. 搜索关键字jquery以查看脚本现在是从 WordPress 的wp-includes文件夹加载的,包括用于向后兼容的jquery-migrate脚本:

<script type='text/javascript' src='http://localhost/
    wp-includes/js/jquery/jquery.js?ver=1.12.4'></script>
<script type='text/javascript' src='http://localhost/
    wp-includes/js/jquery/jquery-migrate.min.js?ver=1.4.1'></script>

它是如何工作的...

本菜谱的关键组件是wp_enqueue_script函数,它允许开发者加载他们自己的 JavaScript 文件或请求 WordPress 加载它附带的一个脚本。虽然加载自己的脚本时该函数需要许多参数,我们将在后面的菜谱中介绍,但它只需要一个参数来加载内置脚本。在这个例子中,该参数是jquery。要获取 WordPress 附带的所有默认脚本的完整列表,请查看该函数的代码参考页面(developer.wordpress.org/reference/functions/wp_enqueue_script/))。

一旦你知道要加载哪个脚本,就应该从以下三个动作钩子之一调用wp_enqueue_script,具体取决于脚本应该加载的目标页面。这些是用于前端页面的wp_enqueue_scripts,用于管理页面的admin_enqueue_scripts,以及用于登录页面的login_enqueue_scripts,其中第一个满足本菜谱的要求。

我们不得不退回去安装一个较旧的主题,以便在激活我们的插件后看到变化。许多现代主题已经请求加载 jQuery,因为它是用于动画菜单或提供许多其他常见功能。话虽如此,如果我们计划将我们的作品分发给更广泛的受众,我们不能假设这一点。

还有更多...

经验丰富的 jQuery 开发者应该知道 WordPress 附带的内容有一个小瑕疵。

jQuery noconflict 模式

为了避免与其他 JavaScript 和 jQuery 库发生内部冲突,WordPress 捆绑的 jQuery 版本被配置在noconflict模式下。这意味着通常可以用来访问 jQuery 的$快捷键将不可用。因此,本章中找到的所有示例都明确指出了 jQuery 关键字。

要重新获取此快捷方式的访问权限,你可以在代码中使用以下语法:

jQuery( document ).ready( function($) { 
    // $ shortcut is now available for this function 
} ); 

使用内置的 ThickBox 插件显示弹出对话框

尽管弹出对话框可能会让访客感到烦恼,但许多网站管理员都在使用这一功能来帮助他们推广特别优惠或让读者订阅他们的内容。由于它在其管理页面中使用了弹出对话框,WordPress 预装了一个名为 ThickBox 的 jQuery 脚本,可用于显示这些类型的对话框。

本食谱展示了如何加载 ThickBox 脚本并使用它来渲染弹出对话框。

准备工作

你应该能够访问 WordPress 开发环境。

如何做到这一点...

  1. 导航到你的开发安装的 WordPress 插件目录。

  2. 创建一个名为 ch9-pop-up-dialog 的新目录。

  3. 导航到目录并创建一个名为 ch9-pop-up-dialog.php 的文本文件。

  4. 在代码编辑器中打开新文件,并在插件文件顶部添加一个适当的标题,将插件命名为 第九章 - 弹出对话框

  5. 将以下代码行添加到注册一个在脚本加载请求时被调用的函数:

add_action( 'wp_enqueue_scripts', 'ch9pud_load_scripts' ); 
  1. 添加以下代码段以提供 ch9pud_load_scripts 函数的实现:
function ch9pud_load_scripts() { 
    wp_enqueue_script( 'jquery' ); 
    add_thickbox(); 
} 
  1. 插入以下代码行以注册一个在页面页脚显示内容的函数:
add_action( 'wp_footer', 'ch9pud_footer_code' ); 
  1. 添加以下代码块以提供 ch9pud_footer_code 函数的实现:
function ch9pud_footer_code() { ?> 
    <script type="text/javascript"> 
    jQuery( document ).ready(function() { 
        setTimeout( function() { 
            tb_show( 'Pop-Up Message', '<?php echo plugins_url( 
                      'content.html?width=420&height=220', 
                      __FILE__ ); ?>', null ); 
        }, 2000 ); 
    } ); 

</script> 
<?php 
} 
  1. 保存并关闭插件文件。

  2. 创建一个名为 content.html 的新 HTML 文件,并在代码编辑器中打开它。

  3. 将以下 HTML 代码作为文件内容插入:

<!DOCTYPE html> 
<html> 
    <body> 
        <div>This is the pop-up content.</div> 
    </body> 
</html> 
  1. 保存并关闭 HTML 文件。

  2. 导航到插件管理页面并激活 第九章 - 弹出对话框 插件。

  3. 访问网站的任何页面,以查看新弹出对话框在整页显示两秒后出现:

图片

它是如何工作的...

与前面的食谱类似,我们首先将一个函数分配给 wp_enqueue_scripts 动作钩子。当执行时,回调函数调用 wp_enqueue_script 以请求从 WordPress 的本地副本加载 jQuery。下一行调用 add_thickbox 函数,这是一个实用函数,它会对 wp_enqueue_scriptwp_enqueue_style 进行多次调用,以在页面页眉中加载适当的 JavaScript 和样式表。

一旦所有必需的元素都加载完毕,菜谱的下一部分将输出一段 JavaScript 代码到页面页脚,该代码使用 jQuery 注册一个函数,该函数将在整个页面加载时被调用。当发生这种情况时,setTimeout JavaScript 函数用于注册一个将在 2000 毫秒后调用的函数,并负责调用 tb_show 来显示弹出对话框。tb_show 有三个参数,第一个参数指示对话框标题,第二个包含要在框内渲染的内容的地址,第三个期望一个要显示的图像组的路径。在我们的情况下,最后一个参数留为空。请注意,对话框的宽度和高度(以像素为单位)作为要显示的内容页面地址的一部分给出。

还有更多...

虽然菜谱显示了一个有效的对话框,但开发者可能希望对如何关闭对话框以及何时显示它有更多的控制。

移除对话框关闭按钮

默认情况下,ThickBox 脚本在弹出对话框的右上角提供了一个关闭按钮,可以随时使用它来关闭对话框。如果您期望访客在关闭对话框之前提供反馈或执行特定操作,这可能不是您想要的。通过将 modal 关键字添加到内容 URL(设置为 true 的值),ThickBox 将移除对话框标题栏,包括关闭按钮:

tb_show( 'Pop-Up Message', '<?php echo plugins_url( 
     'content.html?width=420&height=220&modal=true', __FILE__ ); ?>', 
     null );

一旦关闭按钮消失,我们可以调用 tb_remove JavaScript 函数来关闭对话框。以下是在 content.html 中可以添加的简单链接示例,它将关闭对话框:

<div><a href="#" onclick="tb_remove();">Close Dialog</a></div> 

在选定页面上显示弹出对话框

虽然菜谱的原始代码在网站的每个页面上都显示弹出对话框,但最好只在前页等特定页面上显示,以避免过度展示。为了实现这一点,我们可以将两个 add_action 调用移动到动作钩子回调内部,并在加载我们的脚本之前检查访客是否正在请求查看前页:

add_action( 'template_redirect', 'ch9pud_template_redirect' );

function ch9pud_template_redirect() {
    if ( is_front_page() ) { 
        add_action( 'wp_enqueue_scripts', 'ch9pud_load_scripts' ); 
        add_action( 'wp_footer', 'ch9pud_footer_code' ); 
    }
}

可以使用类似的技术,通过将 is_front_page 函数替换为 is_page( 'id_title_or_slug' ) 函数,该函数检查当前页面的数字 ID、标题或文章缩略语是否与它接收的参数值匹配。在这种情况下,插件配置页面可以允许用户轻松选择一个或多个应显示对话框的页面。

使用短代码控制弹出对话框显示

如您所知,在不必要使用的情况下在页面上加载脚本和样式会减慢该页面的渲染时间,因为浏览器仍然需要下载和验证这些外部文件的内容。虽然先前的菜谱 还有更多... 部分提供了一种选择应加载脚本和样式的特定页面的方法,但另一种方法是通过分析页面内容以查找特殊代码来做出这个决定。

这个菜谱展示了如何向之前的菜谱添加一个过滤器,以在帖子页面中搜索短代码,以决定何时显示弹出对话框。

准备工作

您应该已经遵循了使用内置的 ThickBox 插件显示弹出对话框的菜谱,以便为本菜谱提供一个起点。或者,您可以从代码包中获取结果代码(第九章/ch9-pop-up-dialog/ch9-pop-up-dialog-v1.php),并将文件重命名为ch9-pop-up-dialog.php

如何操作...

  1. 导航到您的开发安装的 WordPress 插件目录。

  2. 导航到ch9-pop-up-dialog目录,然后编辑ch9-pop-up-dialog.php

  3. 找到ch9pud_load_scripts函数,并添加以下突出显示的代码行:

function ch9pud_load_scripts() { 
    // Only load scripts if variable is set to true
 global $load_scripts;

 if ( $load_scripts ) {
        wp_enqueue_script( 'jquery' );
        add_thickbox()
    }
} 
  1. 定位到ch9pud_footer_code函数,并修改代码,将以下突出显示的代码行添加到函数体中:
function ch9pud_footer_code() {
    // Only load scripts if keyword is found on page
 global $load_scripts;
 if ( $load_scripts ) { ?>

    <script type="text/javascript"> 
        jQuery( document ).ready( function() { 
            setTimeout( 
                function(){ 
                    tb_show( 'Pop-Up Message', 
                        '<?php echo plugins_url( 
                        'content.html?width=420&height=220', 
                        __FILE__ ); ?>', null ); 
                }, 2000 ); 
        }); 
    </script> 

<?php }  
} 
  1. 添加以下行代码以注册一个函数,该函数将在任何其他解析和格式化之前过滤帖子页面内容:
add_filter( 'the_posts',
            'ch9pud_conditionally_add_scripts_and_styles' ); 
  1. 添加以下代码块以提供对ch9pud_popup_shortcode函数的实现:

    ch9pud_conditionally_add_scripts_and_styles函数:

function ch9pud_conditionally_add_scripts_and_styles( $posts ) { 
    // Exit function immediately if no posts are present 
    if ( empty( $posts ) ) {
        return $posts;
    }

    // Global variable to indicate if scripts should be loaded 
    global $load_scripts; 
    $load_scripts = false; 

    // Cycle through posts and set flag true if 
    // keyword is found 
    foreach ( $posts as $post ) {         
        $shortcode_pos = stripos( $post->post_content, 
                                  '[popup]', 0 ); 
        if ( $shortcode_pos !== false ) { 
            $load_scripts = true; 
            return $posts; 
        } 
    } 

    // Return posts array unchanged 
    return $posts;     
} 
  1. 插入以下函数调用以声明一个新的短代码以及一个负责将其替换为内容的函数:
add_shortcode( 'popup', 'ch9pud_popup_shortcode' ); 
  1. 将以下代码块添加到为ch9pud_popup_shortcode函数提供一个简单实现的代码块:
function ch9pud_popup_shortcode() { 
    return;     
} 
  1. 保存并关闭插件文件。

  2. 访问网站的前页,您会注意到弹出对话框不再显示。

  3. 创建一个新页面,并在页面内容中插入[popup]短代码。

  4. 查看新页面,以查看新的弹出对话框出现,而[popup]短代码没有显示。

它是如何工作的...

虽然现有的动作钩子首先被修改以创建和查询一个全局变量,以确定是否应该加载脚本并将代码输出到页面页脚,但实际上大部分工作是由与the_posts钩子相关联的过滤器函数完成的。这个函数接收一个数组,包含所有预定要显示的帖子页面,并必须确定是否存在特殊关键字,以适当地设置load_scripts变量。

如您从菜谱的代码中看到的,我们选择查找的文本[popup]是一个短代码。虽然我们可以选择任何文本作为触发显示弹出对话框的触发器,但我们选择了一个短代码,因为它可以通过提供一个简单的渲染函数来轻松消失,该函数返回无内容。

参考以下内容

  • 使用内置的 ThickBox 插件显示弹出对话框的菜谱

使用 Datepicker 插件显示日历日选择器

尽管 WordPress 拥有众多优秀的行政控制面板和用户界面元素,但在日期选择方面仍然采用了一种简单的方法,即让用户通过下拉框和文本字段来选择月份、日期、年份和时间,以指示发布帖子或页面。一种更有趣的方法是使用弹出日历,允许用户通过每个月份的视觉表示进行导航并选择所需的日期。

本食谱展示了如何使用 WordPress 默认提供的 jQuery Datepicker 脚本显示弹出日历,以提供一种轻松选择日期的方法。

准备工作

您应该能够访问 WordPress 开发环境。

如何实现...

  1. 导航到您的开发安装的 WordPress 插件目录。

  2. 创建一个名为ch9-calendar-picker的新目录,以及一个名为css的子目录。

  3. 访问https://www.jqueryui.com/download,取消选中所有组件,仅选择日期选择器。然后,选择 UI lightness 作为要包含的主题,并下载 jQuery UI 包的最新版本。

  4. 使用归档管理工具打开生成的文件,并将文件jquery-ui.min.css提取到新创建的插件目录的css文件夹中。

  5. 将整个images目录从归档中提取到css文件夹中。

  6. 在插件目录中创建一个名为ch9-calendar-picker.php的文本文件。

  7. 在代码编辑器中打开新文件,并在插件文件顶部添加一个合适的标题,将插件命名为“第九章 - 日历选择器”。

  8. 添加以下代码行以注册在脚本加载请求时被调用的函数:

add_action( 'admin_enqueue_scripts', 'ch9cp_admin_scripts' ); 
  1. 添加以下代码段以提供ch9cp_admin_scripts函数的实现:
function ch9cp_admin_scripts() {
    $screen = get_current_screen();
    if ( 'post' == $screen->base && 
         'post' == $screen->post_type ) {
        wp_enqueue_script( 'jquery' ); 
        wp_enqueue_script( 'jquery-ui-core' ); 
        wp_enqueue_script( 'jquery-ui-datepicker' ); 
        wp_enqueue_style( 'datepickercss',  
            plugins_url( 'css/jquery-ui.min.css', 
                   __FILE__ ), array(), '1.12.1' );
    }
} 
  1. 插入以下代码行以注册一个在创建元框时被调用的函数:
add_action( 'add_meta_boxes', 'ch9cp_register_meta_box' ); 
  1. 将以下代码块添加到提供ch9cp_register_meta_box函数实现的代码中:
function ch9cp_register_meta_box() {     
    add_meta_box( 'ch9cp_datepicker_box', 'Assign Date', 
                  'ch9cp_date_meta_box', 'post', 'normal'); 
} 
  1. 插入以下代码块以实现add_meta_box调用中声明的ch9cp_date_meta_box函数:
function ch9cp_date_meta_box( $post ) { ?> 
    <input type="text" id="ch9cp_date" name="ch9cp_date" /> 

    <!-- JavaScript function to display calendar button --> 
    <!-- and associate date selection with field --> 
    <script type='text/javascript'> 
        jQuery( document ).ready( function() { 
        jQuery( '#ch9cp_date' ).datepicker( { minDate: '+0', 
            dateFormat: 'yy-mm-dd', showOn: 'both', 
            constrainInput: true} );  
        } ); 
    </script> 
<?php } 
  1. 保存并关闭插件文件。

  2. 导航到插件管理页面并激活“第九章 - 日历选择器插件”。

  3. 在“帖子管理”部分选择任何项目并编辑它,以查看新的日期分配元框。

  4. 点击...按钮或点击“分配日期”文本框以显示弹出日历并选择一个日期:

图片

工作原理...

就像我们在前面的菜谱中看到的那样,WordPress 附带了许多 jQuery 库。其中两个库,jQuery UI 和 jQuery UI Datepicker,可以用来显示弹出日历并将其与表单上的文本字段关联。话虽如此,这些脚本的分发缺少显示完整渲染日历所需的关联样式表和图像。

这个菜谱首先访问 jQuery UI 网站并下载完整库的副本,其中包括所有必需的布局文件。下载完成后,我们只对获取样式数据的副本感兴趣,因为所有其他必要的脚本都由 WordPress 提供。在通过admin_enqueue_scripts注册函数后,我们在管理页面头部调用三个函数来加载所需的 JavaScript 文件。我们还调用了一个函数来加载我们刚刚下载的样式表。在从下载的存档中复制文件时,我们选择了 CSS 文件的压缩版本,以获得最小的版本。

wp_enqueue_style函数有很多参数。在这个例子中,我们为前四个参数提供了值,以指示样式的名称、样式的路径、一个空的依赖项列表和一个版本号。此函数还有一个第五个参数,我们在这里没有使用,用于指示脚本是否应在头部或页脚加载,默认为头部。

一旦所有必需的脚本都已就绪,代码的其余部分在帖子编辑器中创建一个元数据框,在该框中显示一个文本字段,并输出当页面完全渲染时将被调用的 JavaScript 代码,以将弹出日历与文本字段关联。作为日历选项的一部分,我们指定用户只能通过minDate参数以及所需的日期格式选择未来的日期。

使用 TipTip 插件向管理页面表单字段添加工具提示

文档是插件开发的重要步骤,因为它使用户能够理解如何配置你创建的插件。换句话说,用户通常不会走得太远去寻找他们需要的信息,这导致在讨论论坛或电子邮件中有很多不必要的提问。

如第三章“用户设置和管理页面”中所述,提供文档的一种方法是在插件配置面板的右上角创建一个帮助标签。虽然这种方法对于用户来说比找到 Readme 文件或返回官方 WordPress 插件仓库要容易得多,但它仍然要求他们主动寻找并点击链接来打开该部分。

这就是工具提示发挥作用的地方。使用 jQuery 插件来渲染干净、美观的工具提示,我们可以向插件添加文档,这些文档将根据用户当前交互的配置字段上下文显示。

本食谱展示了如何下载和集成 TipTip jQuery 库,以便在配置字段使用时显示工具提示。

准备工作

您应该已经遵循了 使用 Datepicker 插件显示日历日选择器 的食谱,以便为本食谱提供一个起点。或者,您可以从代码包中获取结果代码(第九章/ch9-calendar-picker/ch9-calendar-picker-v1.php),并将文件重命名为 ch9-calendar-picker.php

如何操作...

  1. 导航到您开发安装的 WordPress 插件目录。

  2. 导航到 ch9-calendar-picker 目录。

  3. 创建一个名为 tiptip 的新子目录。

  4. 访问 TipTip jQuery 主页,网址为 drew.tenderapp.com/kb/tiptip-jquery-plugin/tiptip-downloads

  5. 下载插件源代码的版本 1.3 到您的本地计算机。

  6. 使用归档管理工具打开生成的文件,并将 jquery.tipTip.minified.jstipTip.css 文件提取到 tiptip 目录。

  7. 在代码编辑器中打开主插件文件 ch9-calendar-picker.php

  8. 找到 ch9cp_admin_scripts 函数,并在 if 条件语句的末尾添加以下代码行,以便新脚本仅在页面和帖子编辑器上加载,就像其他脚本一样:

wp_enqueue_script( 'tiptipjs',
                   plugins_url( 'tiptip/jquery.tipTip.minified.js', 
                                __FILE__ ), 
                   array(), '1.3' ); 
wp_enqueue_style( 'tiptip',
                  plugins_url( 'tiptip/tipTip.css', __FILE__ ),
                               array(), '1.3' );
  1. 定位到 ch9cp_date_meta_box 函数,并修改渲染文本框的行,如下所示突出显示的代码:
<input type="text" class="ch9cp_tooltip" 
 id="ch9cp_date" 
       name="ch9cp_date" /> 
  1. 再次,在 ch9cp_date_meta_box 函数中,将以下突出显示的代码块添加到现有的 JavaScript 代码块中:
<script type='text/javascript'> 
    jQuery( document ).ready( function() { 
        jQuery( '#ch9cp_date' ).datepicker( { minDate: '+0', 
                dateFormat: 'yy-mm-dd', showOn: 'both', 
                constrainInput: true } ); 

        jQuery( '.ch9cp_tooltip' ).each( function() { 
 jQuery( this ).tipTip(); 
 } 
 ); 
    }); 
</script> 
  1. 保存并关闭插件文件。

  2. 在帖子管理部分选择任何项目并编辑它。

  3. 将鼠标移至日期字段上,以查看新工具提示的出现:

图片

工作原理...

TipTip 库将常规的 HTML title 标签转换为当用户将鼠标悬停在项目上或选择它时出现的漂亮工具提示。

本食谱首先从插件作者的网站上下载 TipTip 脚本。下载后,我们只提取归档中包含的三个文件中的两个。第三个文件不需要,因为它是一个非压缩版本的脚本。

一旦我们有了所需的文件,我们就在与admin_enqueue_script动作钩子已关联的回调函数中添加对wp_enqueue_scriptwp_enqueue_style函数的调用,将它们加载到管理页面标题中。与wp_enqueue_style类似,wp_enqueue_script函数有五个参数,分别表示脚本的名称、脚本文件的存储位置、脚本的依赖项列表、版本号以及一个选项,用于指示脚本是否应加载到网站标题或页脚中。

一旦库被加载,激活工具提示就相当简单。首先,我们为我们的项目选择一个类名,并将其添加到所有预定与它们关联帮助文本的项目中。然后,我们在每个项目的title标签中添加帮助文本。请注意,相关项目可以是 div、表单输入组件或表格行等任何内容。最后,我们调用一个 jQuery 函数来查找所有具有正确类名的项目,并在它们上执行 TipTip 函数。执行后,所有选定的项目都将显示其标题文本作为工具提示。

参见

  • 使用 Datepicker 插件显示日历日选择器的配方

使用 AJAX 动态更新部分页面内容

当用户创建具有大量动态内容(如 Twitter 小部件或其他获取外部数据的组件)的复杂网站时,每次用户与网站交互时刷新整个页面可能会迅速变成对访客来说令人疲惫不堪的经历。

在这种情况下,使用异步 JavaScript 和 XMLAJAX)可以通过仅在访客页面显示数据子集并动态检索独立部分的更新来大大加快用户导航。更具体地说,AJAX 允许浏览器向 Web 服务器发送请求,包括数据参数,并将它接收到的数据插入到网页中,替换或增强原始内容。

本配方展示了如何将 AJAX 支持添加到第八章中创建的 bug 跟踪系统,创建自定义 MySQL 数据库表

准备工作

你应该已经遵循了第八章,创建自定义 MySQL 数据库表中的从用户文件导入数据到自定义表配方,以便为本配方提供一个起点。或者,你也可以从代码包中获取生成的代码(Chapter 8/ch8-bug-tracker/ch8-bug-tracker-v8.php),并将文件重命名为ch8-bug-tracker.php

如何做到这一点...

  1. 导航到您的开发安装的 WordPress 插件目录。

  2. 导航到ch8-bug-tracker目录并编辑ch8-bug-tracker.php

  3. 定位到ch8bt_shortcode_list函数,并找到准备 SQL 查询的部分。

  4. 在查询中添加额外的行(以下代码块中高亮的代码行)以仅显示打开的 bug(bug_status字段设置为0的 bug):

$bug_query = 'select * from ' . $wpdb->get_blog_prefix(); 
$bug_query .= 'ch8_bug_data '; 
$bug_query .= 'where bug_status = 0 '; 
  1. 将以下代码中突出显示的部分更改到构建搜索查询的代码中:
if ( $search_mode ) { 
    $search_term = '%' . $search_string . '%'; 
    $bug_query .= "and ( bug_title like '%s' "; 
    $bug_query .= "or bug_description like '%s' ) "; 
} 
  1. 找到负责绘制搜索表单的代码,并在其后添加以下突出显示的代码块以显示一个可点击的链接来显示已关闭的错误:
$output .= '</form></div>';

$output .= '<div class="show_closed_bugs">'; 
$output .= 'Show closed bugs'; 
$output .= '</div>';  

$output .= '<div class="bug-tracker-list"><table>'; 
  1. 在错误显示表单之后插入此代码段,以添加提供基于 AJAX 的数据替换功能的 JavaScript:
$output .= "<script type='text/javascript'>"; 
$nonce = wp_create_nonce( 'ch8bt_ajax' );  
$output .= "function replacecontent( bug_status )" . 
           "{ jQuery.ajax( {" . 
           "    type: 'POST', url: ajax_url," . 
           "    data: { action: 'ch8bt_buglist_ajax'," . 
           "            _ajax_nonce: '" . $nonce . "'," . 
           "            bug_status: bug_status }," . 
           "    success: function( data ) {" . 
           "            jQuery('.bug-tracker-list').html( data );" . 
           "            }" . 
           "    });" . 
           "};"; 

$output .= "jQuery( document ).ready( function() {"; 
$output .= "jQuery('.show_closed_bugs').click( function() 
                                    { replacecontent( 1 ); } "; 
$output .= ")});"; 
$output .= "</script>"; 
  1. 在插件文件末尾添加以下代码行以注册一个函数,用于向页面标题添加内容:
add_action( 'wp_head', 'ch8bt_declare_ajaxurl' ); 
  1. 将以下代码块添加到提供ch8bt_declare_ajaxurl函数实现的代码中:
function ch8bt_declare_ajaxurl() { ?> 
    <script type="text/javascript"> 
        var ajax_url = 
            '<?php echo admin_url( 'admin-ajax.php' ); ?>'; 
    </script> 
<?php } 
  1. 插入以下代码行以注册在接收到来自公共用户或已登录用户且操作变量设置为ch8bt_buglist_ajax的 AJAX 请求时将调用的函数:
add_action( 'wp_ajax_ch8bt_buglist_ajax', 'ch8bt_buglist_ajax' ); 
add_action( 'wp_ajax_nopriv_ch8bt_buglist_ajax', 
            'ch8bt_buglist_ajax' ); 
  1. 将以下代码块添加到提供ch8bt_buglist_ajax函数实现的代码中:
function ch8bt_buglist_ajax() { 
    check_ajax_referer( 'ch8bt_ajax' ); 

    if ( isset( $_POST['bug_status'] ) &&
         is_numeric( $_POST['bug_status'] ) ) { 
        global $wpdb; 

        // Prepare query to retrieve bugs from database 
        $bug_query = 'select * from ' . $wpdb->get_blog_prefix(); 
        $bug_query .= 'ch8_bug_data where bug_status = '; 
        $bug_query .= intval( $_POST['bug_status'] ); 
        $bug_query .= ' ORDER by bug_id DESC'; 

        $bug_items = $wpdb->get_results(  
            $wpdb->prepare( $bug_query ), ARRAY_A ); 

        // Prepare output to be returned to AJAX requestor 
        $output = '<div class="bug-tracker-list"><table>'; 

        // Check if any bugs were found 
        if ( $bug_items ) {         
            $output .= '<tr><th style="width: 80px">ID</th>'; 
            $output .= '<th style="width: 300px">'; 
            $output .= 'Title / Desc</th><th>Version</th></tr>'; 

            // Create row in table for each bug 
            foreach ( $bug_items as $bug_item ) { 
                $output .= '<tr style="background: #FFF">'; 
                $output .= '<td>' . $bug_item['bug_id'] . '</td>'; 
                $output .= '<td>' . $bug_item['bug_title']; 
                $output .= '</td><td>' . $bug_item['bug_version']; 
                $output .= '</td></tr>'; 
                $output .= '<tr><td></td><td colspan="2">'; 
                $output .= $bug_item['bug_description']; 
                $output .= '</td></tr>'; 
            } 
        } else { 
            // Message displayed if no bugs are found 
            $output .= '<tr style="background: #FFF">'; 
            $output .= '<td colspan="3">No Bugs to Display</td>'; 
        } 
        $output .= '</table></div><br />';         
        echo $output; 
    }     
    die(); 
} 
  1. 添加以下代码行以注册一个在脚本排队时调用的函数:
add_action( 'wp_enqueue_scripts', 'ch8bt_load_jquery' ); 
  1. 插入以下代码块以提供ch8bt_load_query函数的实现:
function ch8bt_load_jquery() { 
    wp_enqueue_script( 'jquery' );
    wp_enqueue_style( 'bug_tracker_css',
                      plugins_url( 'stylesheet.css', __FILE__ ),
                      array(), '1.0' );
} 
  1. 保存并关闭插件文件。

  2. 在插件目录中创建一个名为stylesheet.css的新文本文件,并在文件中插入以下内容:

.show_closed_bugs {
    cursor: pointer;
    color: #00c;
}
  1. 访问之前创建的错误列表页面,以查看仅显示已打开的错误。

  2. 点击显示已关闭错误的链接,以查看列表如何快速替换为已关闭的问题:

图片

工作原理...

AJAX 页面交互由 JavaScript 代码提供支持,并允许用户创建内容动态更新的页面。为了将此功能添加到我们的错误跟踪系统中,我们首先通过修改现有的短代码错误查询,仅检索具有打开状态(值为0)的条目来开始这个菜谱。

完成此操作后,我们继续添加两个新的元素到初始短代码输出中,一个是显示已关闭错误的链接,另一个是 JavaScript 代码块。链接本身相当简单,包含一个类名和一个文本标签,访客可以点击。JavaScript 代码稍微复杂一些。基本上,脚本会在访客点击show_closed_bugs链接时请求调用replacecontent函数。反过来,replacecontent函数包含对 jQuery ajax函数的单次调用。此函数接受多个参数,从操作类型开始,设置为POST。这表示请求 URL 中发送的所有变量都将存储在标准的$_POST变量数组中。

第二个参数是要发送请求的 URL。这里使用的变量在ch8bt_declare_ajaxurl函数生成的标题代码中定义,指向 WordPress 的admin-ajax.php脚本 URL。虽然此脚本的名称以单词admin开头,但它也可以用于处理访客页面上的 AJAX 请求。

在这两个初始参数之后是一个包含多个数据元素的 data 数组,例如动作名称、用于保护请求的 nonce 字段以及应检索的错误状态。最后,success 参数表示从 AJAX 请求返回的数据应用于替换现有页面中 bug-tracker-list div 部分的 HTML 内容。

为了处理这个请求,我们的插件继续注册 ch8bt_buglist_ajax 函数,以便在匹配以下两个变量名操作之一时被调用:wp_ajax_<actionname>wp_ajax_nopriv_<actionname>。在两种情况下,<actionname> 是作为 AJAX 请求中的数据参数发送的字符串。在收到请求后,回调函数生成一个更新的错误表,回显生成的 HTML 代码,并调用标准的 PHP die() 函数。虽然这一步可能看起来有些奇怪,但它是为了避免在新的 HTML 末尾出现尾随的 1,这表明 WordPress 成功执行了 AJAX 处理。

虽然 ch8bt_buglist_ajax 函数与现有的 ch8bt_shortcode_list 函数共享大量代码,但创建一个只包含此示例所需元素的独立代码块会更简单。话虽如此,合并这两个函数将使未来更新表格布局更容易维护。

参见

  • 在第八章 创建自定义 MySQL 数据库表 中的 从用户文件导入到自定义表 菜单

第十章:将新小部件添加到 WordPress 库

在本章中,您将通过以下主题了解如何创建自己的小部件:

  • 在 WordPress 中创建新小部件

  • 显示配置选项

  • 验证配置选项

  • 实现小部件显示函数

  • 添加自定义仪表板小部件

  • 将自定义小部件添加到网络仪表板

简介

小部件自 WordPress 平台早期以来就存在。它们允许用户轻松地将由 WordPress 本身(帖子或页面数据)或已安装的任何插件(例如,错误跟踪系统信息)提供的内容块填充到他们的网站主题的侧边栏或其他区域。查看 WordPress 安装,默认的小部件集包括存档小部件,它列出月度帖子存档,以及最近评论小部件,它提供了一个简单的方式来显示存储在您的 WordPress 网站上的访客评论。

遵循其开放设计,WordPress 提供了允许插件开发者创建新小部件的函数,用户可以将这些小部件添加到他们的页面设计中。本章展示了如何使用小部件类创建自定义小部件。它还涵盖了第二种类型的小部件,仪表板小部件,可以用于在管理区域的前页上显示插件特定的信息。

在 WordPress 中创建新小部件

创建自定义小部件的第一步是定义其名称,并指出哪个类包含所有实现函数。一旦新元素在系统中注册,它将立即出现在小部件列表中,用户可以将其拖放到侧边栏。

此配方定义了一个新小部件,用于显示来自第四章,“自定义帖子类型的威力”中创建的自定义帖子类型分类的最新书评。

准备工作

您应该已经遵循了第四章,“自定义帖子类型的威力”中的使用插件过滤器更新页面标题以包含自定义帖子数据配方,以便为本配方提供一个起点。或者,您可以从代码包中获取结果代码(Chapter 4/ch4-book-reviews/ch4-book-reviews-v11.php)并激活第四章 - 书评插件。

如何操作...

  1. 导航到您的开发安装的 WordPress 插件目录。

  2. 创建一个名为ch10-book-review-widget的新目录。

  3. 导航到目录并创建一个名为ch10-book-review-widget.php的文本文件。

  4. 在代码编辑器中打开新文件,并在插件文件顶部添加一个适当的标题,命名为“第十章 - 书评小部件”。

  5. 添加以下代码行以注册一个在初始化小部件时被调用的函数:

add_action( 'widgets_init', 'ch10brw_create_widgets' ); 
  1. 添加以下代码段以提供ch10brw_create_widgets函数的实现:
function ch10brw_create_widgets() { 
    register_widget( 'Book_Reviews' ); 
} 
  1. 插入以下代码块以声明Book_Reviews类及其构造函数:
class Book_Reviews extends WP_Widget { 
    // Construction function 
    function __construct () { 
        parent::__construct( 'book_reviews', 'Book Reviews',
            array( 'description' =>                                     
                   'Displays list of recent book reviews' ) ); 
    } 
} 
  1. 保存并关闭插件文件。

  2. 导航到插件管理页面并激活第十章 - 图书评论小工具插件。

  3. 访问外观管理页面的小工具部分,以查看新创建的图书评论小工具作为可用小工具列表的一部分出现。

  4. 将新小工具拖放到右侧列出的可用侧边栏之一以创建小工具实例,并查看当前小工具没有可配置的选项:

图片

它是如何工作的...

widgets_init动作钩子用于注册一个在 WordPress 创建小工具时要执行的功能。当回调发生时,我们通过调用简单的register_widget函数创建一个新的小工具。如菜谱所示,此函数需要一个参数,表示包含小工具定义的类的名称。

菜谱的其余部分声明了小工具实现类,该类扩展了 WordPress 的WP_Widget类。虽然该类有许多潜在的成员方法,但本菜谱仅定义了类构造函数,该构造函数通过指定一个唯一标识符、一个标题以及嵌入在可选参数数组中的描述来初始化对象实例。与插件中声明的任何其他函数一样,为小工具类和小工具标识符提供唯一名称以避免与其他插件冲突是很重要的。

当插件激活时,用户可以立即看到新小工具,并且能够将新元素的一个或多个实例作为侧边栏内容的一部分添加。然而,直到本章稍后的菜谱中实现了其widget方法,新小工具在网站页面上除了错误消息之外不会渲染任何内容。

更多内容...

如您所注意到的,这个菜谱创建了一个与在第四章中创建的主要图书评论插件分开的插件文件和目录,自定义文章类型的威力

扩展其他插件的插件

虽然我们可以将小工具创建代码放在与图书评论插件相同的文件中,但将其放在单独的文件中同样有效。实际上,官方wordpress.org仓库中的一些插件确实使用这种技术将它们的功能分解成更易管理的代码段。使用这种技术时,需要注意确保在回调函数中引用它们之前,所有次要插件所依赖的元素都已加载。

在这种情况下,由于小工具是在 WordPress 初始化过程的后期创建的,因此小工具将需要的自定义文章类型将可用。

参见

  • 第四章,自定义文章类型的威力中的使用插件过滤器更新页面标题以包含自定义文章数据菜谱

显示配置选项

与插件配置页面类似,小部件可以有一个或多个选项,允许用户指定组件的一些方面应该如何表现。这些选项可以为添加到网站布局的每个小部件实例单独配置。为了处理所有关于多个可能的小部件实例的物流,WordPress 实际上负责大多数数据处理和存储任务。

本食谱展示了如何向书评小部件类添加新方法以显示配置选项。

准备工作

您应该已经遵循了在 WordPress 中创建新小部件的食谱,以便为本食谱提供一个起点。或者,您可以从代码包中获取结果代码(第十章/ch10-book-review-widget/ch10-book-review-widget-v1.php),并将文件重命名为ch10-book-review-widget.php

如何操作...

  1. 导航到您的开发安装的 WordPress 插件目录。

  2. 导航到ch10-book-review-widget目录并编辑ch10-book-review-widget.php

  3. Book_Reviews类中找到并添加以下代码块以定义form方法:

function form( $instance ) { 
    // Retrieve previous values from instance 
    // or set default values if not present 
    $render_widget = ( !empty( $instance['render_widget'] ) ? 
                       $instance['render_widget'] : 'true' ); 

    $nb_book_reviews = ( !empty( $instance['nb_book_reviews'] ) ? 
                         $instance['nb_book_reviews'] : 5 ); 

    $widget_title = ( !empty( $instance['widget_title'] ) ?  
                      esc_attr( $instance['widget_title'] ) : 
                      'Book Reviews' );            
    ?>

    <!-- Display fields to specify title and item count --> 
    <p> 
        <label for="<?php echo  
                    $this->get_field_id( 'render_widget' ); ?>"> 
        <?php echo 'Display Widget'; ?>             
        <select id="<?php echo 
                     $this->get_field_id( 'render_widget' ); ?>"
                name="<?php echo 
                $this->get_field_name( 'render_widget' ); ?>"> 
            <option value="true" 
                <?php selected( $render_widget, 'true' ); ?>> 
            Yes</option> 
            <option value="false" 
                <?php selected( $render_widget, 'false' ); ?>> 
            No</option> 
        </select>                     
        </label> 
    </p> 
    <p> 
        <label for="<?php echo  
                    $this->get_field_id( 'widget_title' ); ?>"> 
        <?php echo 'Widget Title:'; ?>             
        <input type="text"  
               id="<?php echo 
                    $this->get_field_id( 'widget_title' );?>"
               name="<?php
               echo $this->get_field_name( 'widget_title' ); ?>" 
               value="<?php echo $widget_title; ?>" />             
        </label> 
    </p>  
    <p> 
        <label for="<?php echo  
                    $this->get_field_id( 'nb_book_reviews' ); ?>"> 
        <?php echo 'Number of reviews to display:'; ?>             
        <input type="text"  
               id="<?php echo 
                   $this->get_field_id( 'nb_book_reviews' ); ?>"
               name="<?php echo 
               $this->get_field_name( 'nb_book_reviews' ); ?>" 
               value="<?php echo $nb_book_reviews; ?>" />             
        </label> 
    </p> 
<?php } 
  1. 保存并关闭插件文件。

  2. 刷新外观 | 小部件管理页面,并展开书评小部件实例以查看新创建的选项。

  3. 修改小部件选项并点击保存以更新其配置:

图片

它是如何工作的...

当用户创建一个新的小部件实例时,WordPress 会自动管理该元素的配置选项,使用一个数组变量。如果存在,它还会调用小部件类的form方法,以在配置面板中渲染小部件实例的选项。

form方法中的前几行代码验证instance数组是否包含适当的值,指定小部件是否应该显示,要显示的书籍评论数量,以及小部件开始时应显示的标题。如果这些选项中的任何一个缺失,我们使用 PHP 三元条件运算符(?:)为render_widgetnb_book_reviewswidget_title函数分配默认值。此运算符期望三个表达式,顺序如下:(expr1)?(expr2):(expr3)。如果expr1为真,则返回expr2;如果为假,则返回expr3

在这些变量就绪后,form方法的其余代码使用 HTML 和 PHP 代码的混合来渲染在小部件编辑器中显示的配置字段。在整个代码中看到的get_field_idget_field_name方法用于生成唯一的标识符,这将帮助 WordPress 为所有小部件实例分别存储数据。

如本菜谱所示,部件类能够自动处理和保存部件配置参数。然而,需要注意的是,允许 WordPress 自行处理此任务意味着不会对输入的数据进行验证。如果用户输入文本而不是要显示的评论数量,这可能会导致问题。下一个菜谱将展示如何处理数据验证。

另请参阅

  • 在 WordPress 中创建新部件菜谱

验证配置选项

在前一个菜谱中放置的部件配置面板是功能性的,允许用户更改选项并在网站数据库中保存更新后的值。话虽如此,WordPress 默认情况下在用户保存部件时所做的只是直接将值存储到网站数据库中。由于盲目接受用户数据可能会导致功能问题和安全风险,如果输入了错误或恶意值,因此最好通过创建一个能够验证配置数据在保存之前的update方法来添加数据验证规则。本菜谱展示了如何实现部件的update方法。

准备工作

您应该已经遵循了显示配置选项菜谱,以便为本菜谱提供一个起点。或者,您可以从代码包中获取结果代码(第十章/ch10-book-review-widget/ch10-book-review-widget-v2.php)并将文件重命名为ch10-book-review-widget.php

如何操作...

  1. 导航到您的开发安装的 WordPress 插件目录。

  2. 导航到ch10-book-review-widget目录并编辑ch10-book-review-widget.php

  3. 找到Book_Reviews类,并在类内添加以下代码块以定义update方法:

function update( $new_instance, $instance ) { 
    // Only allow numeric values 
    if ( is_numeric ( $new_instance['nb_book_reviews'] ) ) {
        $instance['nb_book_reviews'] =
            intval( $new_instance['nb_book_reviews'] );
    } else {
        $instance['nb_book_reviews'] =
            $instance['nb_book_reviews'];
    }

    $instance['widget_title'] =
        sanitize_text_field( $new_instance['widget_title'] ); 

    $instance['render_widget'] = 
        sanitize_text_field( $new_instance['render_widget'] );      

    return $instance; 
}    
  1. 保存并关闭插件文件。

  2. 访问外观管理页面的部件部分并展开书评部件实例。

  3. 在要显示的评论数量字段中输入文本值并保存部件。您会看到该字段的值会恢复为该字段保存的最后一个有效数字。

它是如何工作的...

update方法接收两个数据数组,并必须返回一个数组以保存到网站数据库中。这两个传入的数组分别包含用户输入的新选项值和部件之前存储的值。

要从已知值开始,方法实现首先将旧值复制到一个名为$instance的新变量中。然后通过调用sanitize_text_field函数来初始化,该函数从文本字段中移除可能有害的 HTML 或 PHP 标签,并将返回值保存到$instance数组中。它还在进入时调用 PHP 的is_numericintval函数,以指示要显示的评论数量,确保它是一个数值。如果输入的不是数字,则将保存并显示之前的字段值给用户。不幸的是,当执行此类小部件选项验证时,无法显示错误消息。

相关阅读

  • 显示配置选项配方

实现小部件显示函数

对于我们迄今为止所做的所有小部件创建工作,我们的新创建在网站上还没有显示任何内容。当显示包含小部件的区域时,WordPress 会尝试为每个用户选择的每个小部件调用名为widget的方法,以便将所需的内容输出到浏览器。

这个配方展示了如何在侧边栏中实例化小部件时,如何实现一个widget方法来显示最近的书评列表。

准备工作

您应该已经遵循了验证配置选项配方,以便为本配方提供一个起点。或者,您可以从代码包中获取结果代码(第十章/ch10-book-review-widget/ch10-book-review-widget-v3.php),并将文件重命名为ch10-book-review-widget.php

如何做...

  1. 导航到您的开发安装的 WordPress 插件目录。

  2. 导航到ch10-book-review-widget目录并编辑ch10-book-review-widget.php

  3. 在类Book_Reviews中找到并添加以下代码块以定义widget方法:

function widget( $args, $instance ) { 
    if ( 'true' == $instance['render_widget'] ) { 
        // Extract members of args array as individual variables 
        extract( $args );

        // Retrieve widget configuration options 
        $nb_book_reviews = 
             ( !empty( $instance['nb_book_reviews'] ) ? 
                       $instance['nb_book_reviews'] : 5 ); 

        $widget_title = ( !empty( $instance['widget_title'] ) ?  
                          esc_attr( $instance['widget_title'] ) : 
                          'Book Reviews' ); 

        // Preparation of query string to retrieve book reviews 
        $query_array = array( 'post_type' => 'book_reviews', 
                              'post_status' => 'publish', 
                              'posts_per_page' => 
                                  $nb_book_reviews ); 

        // Execution of post query 
        $book_review_query = new WP_Query(); 
        $book_review_query->query( $query_array );  

        // Display widget title 
        echo $before_widget . $before_title; 
        echo apply_filters( 'widget_title', $widget_title ); 
        echo $after_title;  

        // Check if any posts were returned by query 
        if ( $book_review_query->have_posts() ) {             
            // Display posts in unordered list layout 
            echo '<ul>'; 

            // Cycle through all items retrieved 
            while ( $book_review_query->have_posts() ) { 
                $book_review_query->the_post(); 
                echo '<li><a href="' . get_permalink() . '">'; 
                echo get_the_title( get_the_ID() ) . '</a></li>'; 
            } 
            echo '</ul>';  
        } 
        wp_reset_query(); 
        echo $after_widget; 
    } 
} 
  1. 保存并关闭插件文件。

  2. 访问网站的前页以查看新添加的小部件内容在侧边栏中显示:

图片

它是如何工作的...

与我们在早期章节中看到的动作钩子类似,widget方法旨在直接输出 HTML 代码到浏览器,当在侧边栏中创建新小部件的实例时,这些代码将在浏览器中显示。

widget方法首先检查小部件是否应该显示。如果应该显示,它将继续通过在接收到的第一个参数上调用标准的 PHP extract函数,即名为$args的数组,来继续操作。调用此函数解析数组并为每个找到的元素创建变量,这使得后续代码更容易访问应该放置在标题和小部件内容之前和之后的元素。

在这个初始声明之后,配方继续通过从$instance数组中检索要显示的项目数量和小工具标题,该数组作为第二个方法参数使用与实现form方法时相同的技巧接收,来继续。

其余的代码与在第四章自定义帖子类型的威力(在短代码中显示自定义帖子类型数据)中创建的书评短代码非常相似,在那里我们组装一个查询字符串,指示从数据库中检索的数据类型和最大数量。通过创建 WordPress WP_Query对象的新实例来执行生成的查询。如果找到结果,以下配方代码将遍历所有条目并输出代码以渲染找到的所有项目的无序列表。最后,但同样重要的是,配方通过输出$before_widget$after_widget$before_title$after_title小工具类变量以及用户指定的正确位置的小工具标题来格式化小工具内容。

相关内容

  • 在 WordPress 中创建新小工具的配方

添加自定义仪表板小工具

虽然小工具主要用于网站管理员轻松地将内容添加到他们的前端网站,但 WordPress 还包含另一种插件开发者可以使用以增强用户体验的小工具。仪表板插件是出现在网站管理区域首页的板块。这些板块可以提供任何类型的功能,从简单显示存储在插件中的数据量到允许网站管理员快速执行配置任务表单。

本配方展示了如何添加一个新仪表板小工具,该小工具指示系统中存储了多少书评,并提供快速访问它们的链接。

准备工作

你应该已经遵循了来自第四章,自定义帖子类型的威力中的使用插件过滤器更新页面标题以包含自定义帖子数据配方,以便为本配方提供一个起点。或者,你可以从代码包中获取结果代码(第四章/ch4-book-reviews/ch4-book-reviews-v11.php),并激活第四章 - 书评插件。

如何操作...

  1. 导航到你的开发安装的 WordPress 插件目录。

  2. 创建一个名为ch10-book-review-dashboard-widget的新目录。

  3. 导航到该目录,创建一个名为ch10-book-review-dashboard-widget.php的文本文件。

  4. 在代码编辑器中打开新文件,并在插件文件顶部添加一个适当的标题,将插件命名为第十章 - 书评仪表板小工具

  5. 添加以下代码行以注册在准备仪表板内容时调用的函数:

add_action( 'wp_dashboard_setup', 
            'ch10brdw_add_dashboard_widget' ); 
  1. 添加以下代码段以提供ch10brdw_add_dashboard_widget函数的实现:
function ch10brdw_add_dashboard_widget() { 
    wp_add_dashboard_widget( 'book_reviews_dashboard_widget', 
                             'Book Reviews', 
                             'ch10brdw_dashboard_widget' ); 
} 
  1. 插入以下代码块以实现之前步骤中声明的ch10brdw_dashboard_widget函数:
function ch10brdw_dashboard_widget() {  
    $book_review_count = wp_count_posts( 'book_reviews' ); 
    if ( !empty( (array) $book_review_count ) ) {
    ?> 
    <a href="<?php echo add_query_arg( array(  
                                  'post_status' => 'publish', 
                                  'post_type' => 'book_reviews' ), 
                                  admin_url( 'edit.php' ) ); ?>"> 
    <strong> 
          <?php echo $book_review_count->publish; ?> 
    </strong> Published 
    </a> 
    <br /> 
    <a href="<?php echo add_query_arg( array(  
                                  'post_status' => 'draft', 
                                  'post_type' => 'book_reviews' ),  
                                  admin_url( 'edit.php' ) ); ?>"> 
    <strong> 
        <?php echo $book_review_count->draft; ?> 
    </strong> Draft 
    </a> 
<?php }
}
  1. 保存并关闭插件文件。

  2. 导航到插件管理页面并激活第十章 - 书评仪表板小部件插件。

  3. 导航到网站的仪表板,查看页面底部的新的书评小部件,如下一个屏幕截图所示:

图片

它是如何工作的...

任何插件都可以在 WordPress 组合此管理登录页面内容时注册自己的仪表板小部件。在注册一个在仪表板设置阶段被调用的函数后,我们的配方会调用wp_add_dashboard_widget函数,在回调执行时将我们的元素添加到网站上。wp_add_dashboard_widget函数需要三个参数,这些参数需要为新项目提供一个唯一的标识符,一个要在小部件顶部显示的标题,以及一个将负责生成小部件内容的函数。wp_add_dashboard_widget函数还有一个可选的第四个参数,可以在小部件需要作为仪表板小部件内容的一部分处理表单数据时使用。

如前一个屏幕截图所示,仪表板小部件是通过 WordPress 元框显示的,其中内容显示函数直接回显的任何 HTML 代码都直接出现在框中。

虽然显示函数主要由 HTML 代码组成,但我们还调用了wp_count_posts实用函数,该函数可以轻松返回给定文章类型的帖子数量。

新小部件可以被隐藏并移动到仪表板上的新位置,就像任何其他内置小部件一样。就像本章早期创建的前端小部件插件一样,需要注意的是,此插件中的所有代码都位于一个单独的文件中,与在第四章,自定义文章类型的威力中创建的原始书评插件文件分开,以将代码单独组织在原始插件文件之外。

参见

  • 在第四章,自定义文章类型的威力中,查看使用插件过滤器更新页面标题以包含自定义文章数据的配方。

向网络仪表板添加自定义小部件

如同在标题为创建网络级别管理页面的菜谱中讨论的那样,该菜谱位于第三章用户设置和管理页面,WordPress 提供了一个非常强大的模式,称为网络模式,它允许从单个平台安装中为多个网站提供服务。当创建插件时,开发者需要考虑他们的插件是否应该提供一个仪表板小部件,该小部件仅在网络管理员的仪表板中可见,而不是在单个网站仪表板中可见,或者他们的插件的范围是否真的更相关于每个网站的级别。以下菜谱展示了如何修改之前定义的仪表板小部件,以便在网络安装上出现在网络管理面板中,同时在单个站点安装的行政仪表板中仍然可见。

准备工作

您应该已经遵循了添加自定义仪表板小部件菜谱,以便为本菜谱提供一个起点。或者,您可以从代码包中获取结果代码(Chapter 10/ch10-book-review-dashboard-widget/ch10-book-review-dashboard-widget-v1.php),并将文件重命名为ch10-book-review-dashboard-widget.php。您还应该能够访问一个配置为网络的网络站点,以测试此插件的所有功能。

如何操作...

  1. 导航到您的开发安装的 WordPress 插件目录。

  2. 创建一个名为ch10-book-review-dashboard-widget的新目录,并编辑ch10-book-review-dashboard-widget.php

  3. 定位到最初在插件顶部添加的add_action调用,并在现有函数调用周围添加以下突出显示的代码行:

if ( is_multisite() ) {
 add_action( 'wp_network_dashboard_setup',
                'ch10brdw_add_dashboard_widget' );
} else {
    add_action( 'wp_dashboard_setup', 
                'ch10brdw_add_dashboard_widget' );
}
  1. 定位到ch10brdw_dashboard_widget函数,并在函数的现有实现周围添加以下代码。新的代码行以粗体显示:
function ch10brdw_dashboard_widget() { 
    if ( is_multisite() ) {
 $sites_list = get_sites();
 } else {
 $sites_list = array( 'blog_id' => 1 );
 }

 foreach( $sites_list as $site ) {
 if ( is_multisite() ) {
 switch_to_blog( $site->blog_id );
 }
 $site_name = get_bloginfo( 'name' );
 echo '<div>' . $site_name . '</div>';  
        $book_review_count = wp_count_posts( 'book_reviews' ); 
        ?> 
        // REST OF PREVIOUS CODE GOES HERE
        <?php }
    }
 if ( is_multisite() ) {
 restore_current_blog();
 }
} 
  1. 保存并关闭插件文件。

  2. 导航到网站仪表板(在单个站点安装上)或网络级别仪表板(在网络安装上),以查看之前相同的仪表板或分别查看网络级别仪表板:

图片

它是如何工作的...

在这个菜谱中进行的初始更改是检查插件是否运行在单个站点的 WordPress 安装或网络安装上,并根据结果将回调函数与适当的行为钩子关联。正如您所看到的,我们在两种情况下都使用了相同的回调函数,因为无论在哪种情况下,小部件注册函数都应该调用wp_add_dashboard_widget来注册一个与系统相关联的小部件。

小工具渲染函数在两种模式下也是共享的。在网络安装上运行时,渲染代码首先通过get_sites函数获取所有站点的列表,然后遍历站点列表,并使用switch_to_blog函数访问每个站点的数据库表中的数据。一旦处理完所有博客,我们使用restore_current_blog函数回到配置为网络顶级站点的原始站点。

恢复当前博客非常重要,以避免留下一些内部变量指向错误的站点。

当在单个站点的 WordPress 安装上运行时,我们创建一个包含单个条目的虚拟站点列表,以便我们能够使用相同的foreach循环控制结构。然后,当 WordPress 未配置为多站点模式时,我们避免调用与网络站点操作相关的函数。否则,查询有多少书评存在并显示它们的实际代码在两个版本的插件中是相同的。

参见

  • 添加自定义仪表板小工具 菜谱

第十一章:启用插件国际化

在本章中,您将通过以下主题了解插件本地化:

  • 修改 WordPress 语言配置

  • 适配默认用户设置以进行翻译

  • 使管理页面代码准备好翻译

  • 修改短代码输出以进行翻译

  • 使用 Poedit 翻译文本字符串

  • 在插件初始化中加载语言文件

简介

WordPress 是一个全球现象,用户遍布全球。为了为不同地区的用户提供更具体的使用体验,WordPress 提供了翻译所有面向用户和访客内容的能力,从而使得众多本地化版本可供在线下载。像平台上的大多数其他功能一样,国际化也通过一系列易于使用的函数提供给插件开发者——主要区别在于,插件翻译通常包含在扩展中,而不是像 WordPress 那样单独下载。

为了使他们的插件能够本地化,开发者在处理文本元素时必须使用特殊的国际化函数。一旦这种结构建立,任何用户都可以为自己所知的语言创建本地化版本,并将它们提交给插件作者,以便在未来的扩展更新中包含在内。WordPress.org 还提供了一个在线界面,让用户能够在不使用离线工具的情况下贡献翻译。

本章解释了如何准备插件以进行翻译,并展示了如何使用 Poedit 工具为简单插件创建新的语言文件。

修改 WordPress 语言配置

翻译插件的第一步是将 WordPress 配置为非英语的语言设置。这将自动触发平台中的机制,以寻找任何国际化字符串的替代语言内容。

在这个菜谱中,我们将设置网站为法语。

准备工作

您应该能够访问 WordPress 开发环境。

如何操作...

  1. 打开您的网站仪表板,并导航到设置菜单的常规部分。

  2. 将网站语言设置为加拿大法语并点击保存更改。

它是如何工作的...

WordPress 的“站点语言”配置选项允许您选择访客访问您的网站时将看到的语言。如语言下拉列表所示,WordPress 默认安装了单一语言,显示在列表顶部。列表的其余部分表示可以添加到您网站的语言。当您设置新语言时,将自动下载所选语言的翻译文件,并且新选择成为访客的默认语言。如果您查看 WordPress 安装的wp-content/languages目录,您将看到为所选语言下载了多个具有.po.mo扩展名的文件。便携对象(.po)文件是包含 WordPress 源代码中所有原始英文文本的 ASCII 文本文件,而机器对象(.mo)文件是编译成二进制版本的翻译表,以便在网站渲染时由 PHP 高效加载。

如果在仪表板的“常规”部分没有提供要自动安装的语言列表,请访问codex.wordpress.org/Installing_WordPress_in_Your_Language了解如何手动下载和安装 WordPress 的翻译文件。

适配默认用户设置以进行翻译

在我们能够为我们的插件创建翻译文件之前,它们的代码需要特别编写,以便允许文本项进行翻译。这项工作从插件的激活例程开始,在那里设置默认插件选项值,以便在网站配置文件中指定了除英语以外的语言时找到替代值。

这个配方展示了如何在初始化时将翻译字符串分配给插件默认选项数组。

准备工作

您应该已经遵循了“更改 WordPress 语言配置”配方,以便为网站指定翻译语言。

如何做到这一点...

  1. 导航到您的开发安装的 WordPress 插件目录。

  2. 创建一个名为ch11-hello-world的新目录。

  3. 导航到该目录并创建一个名为ch11-hello-world.php的文本文件。

  4. 在代码编辑器中打开新文件,并在插件文件顶部添加一个适当的标题,命名为“第十一章 - Hello World”。

  5. 添加以下代码行以注册在插件激活时调用的函数:

register_activation_hook( __FILE__, 
                         'ch11hw_set_default_options_array' ); 
  1. 插入以下代码块以提供ch11hw_set_default_options_array函数的实现:
function ch11hw_set_default_options_array() { 
    if ( false === get_option( 'ch11hw_options' ) ) { 
        $new_options = array(); 
        $new_options['default_text'] = __( 'Hello World',
                                           'ch11hw_hello_world' ); 
        add_option( 'ch11hw_options', $new_options ); 
    }  
} 
  1. 保存并关闭插件文件。

  2. 导航到扩展(法语中的插件)管理页面,并点击Chapter 11 - Hello World插件的“激活”(法语中的“激活”)链接。

  3. 使用 phpMyAdmin,找到options表条目,其中option_name字段的值为ch11hw_options以查看新创建的选项:

图片

它是如何工作的...

__函数(即两个下划线)是一个 WordPress 实用函数,它试图在其第二个参数指定的文本域中找到其第一个参数接收到的文本的翻译。文本域基本上是由 WordPress 管理的全局翻译表的一个子部分。在这个例子中,要翻译的文本是字符串Hello World,系统试图在该ch11hw_hello_world域中找到其翻译。由于该域目前不可用,该函数返回其第一个参数接收到的原始字符串。插件代码将该值分配给默认配置数组。

应当注意,__函数实际上是对translate函数的一个别名。虽然这两个函数具有相同的功能,但当代码中包含大量需要翻译的文本元素时,使用__可以使代码更短。

虽然开发者可能会倾向于在__函数的第一个参数中使用变量或常量来显示相同的文本多次,但这不应该这样做,因为这会导致翻译查找机制出现问题。

参见

  • 更改 WordPress 语言配置配方

准备管理页面代码以供翻译

虽然前面的配方展示了如何在插件代码中查找文本项的翻译并返回其值以供进一步处理,但在许多情况下,立即显示翻译内容更为实用。

这个配方展示了如何翻译简单管理页面的内容以供立即显示。

准备工作

您应该已经遵循了调整默认用户设置以进行翻译配方,以便为本配方提供一个起点。或者,您可以从代码包中获取该配方的结果代码(第十一章/ch11-hello-world/ch11-hello-world-v1.php)。在开始之前,您应该将文件重命名为ch11-hello-world.php

如何做到...

  1. 导航到您开发安装中 WordPress 插件目录的ch11-hello-world文件夹。

  2. 在文本编辑器中打开ch11-hello-world.php文件。

  3. 在文件末尾添加以下行代码以注册一个在 WordPress 构建管理页面菜单时被调用的函数:

add_action( 'admin_menu', 'ch11hw_settings_menu' ); 
  1. 添加以下代码部分以提供ch11hw_settings_menu函数的实现:
function ch11hw_settings_menu() { 
    add_options_page( 
        __( 'Hello World Configuration', 'ch11hw_hello_world' ), 
        __( 'Hello World', 'ch11hw_hello_world' ), 
       'manage_options', 
       'ch11hw-hello-world', 'ch11hw_config_page' ); 
} 
  1. 插入以下代码块以创建在add_options_page调用中声明的ch11hw_config_page函数:
function ch11hw_config_page() { 
    $options = get_option( 'ch11hw_options' ); 
    ?> 

    <div id="ch11hw-general" class="wrap"> 
    <!-- Echo translation for "Hello World" to the browser --> 
    <h2><?php _e( 'Hello World', 'ch11hw_hello_world' ); ?></h2> 

    <form method="post" action="admin-post.php"> 
    <input type="hidden" name="action" 
           value="save_ch11hw_options" /> 
    <?php wp_nonce_field( 'ch11hw' ); ?> 

    <!-- Echo translation for "Hello World" to the browser --> 
    <?php _e( 'Default Text', 'ch11hw_hello_world' ); ?>: 
    <input type="text" name="default_text" 
           value="<?php echo 
                   esc_html( $options['default_text'] ); ?>"/> 
    <br /> 
    <input type="submit" value="<?php _e( 'Submit', 
        'ch11hw_hello_world' ); ?>" class="button-primary"/> 
    </form> 
    </div> 
<?php } 
  1. 添加以下行代码以注册一个在准备显示管理面板时执行的函数:
add_action( 'admin_init', 'ch11hw_admin_init' ); 
  1. 将以下代码段附加到提供ch11hw_admin_init函数实现的代码:
function ch11hw_admin_init() { 
     add_action( 'admin_post_save_ch11hw_options',
                 'process_ch11hw_options' ); 
} 
  1. 提供在上一步骤中声明的process_ch11hw_options函数的代码,通过插入以下代码:
function process_ch11hw_options() { 
    if ( !current_user_can( 'manage_options' ) ) {
          wp_die( 'Not allowed' );
    }

    check_admin_referer( 'ch11hw' );

    $options = get_option( 'ch11hw_options' );     
    $options['default_text'] = $_POST['default_text']; 

    update_option( 'ch11hw_options', $options ); 
    wp_redirect( add_query_arg( 'page', 'ch11hw-hello-world', 
                     admin_url( 'options-general.php' ) ) );
    exit; 
} 
  1. 保存并关闭插件文件。

  2. 导航到您开发 WordPress 安装的管理页面。

  3. 点击左侧导航菜单中的“设置”(法语中的“Reglages”)部分以展开它。您将在树中看到一个名为“Hello World”的新菜单项。选择新条目将显示插件简单的配置表单,如下面的截图所示:

图片

它是如何工作的...

本食谱使用了之前食谱中提到的__函数,以及_e函数。第二个函数的目的是与__类似,但它会立即将翻译查找的结果输出到浏览器。它应该用于所有之前只是简单文本的 HTML 代码中的文本元素。当然,调用此函数需要标准打开和关闭 PHP 标签(<??>)在周围的 HTML 中执行。

本插件代码的其余部分负责将用户更新存储在网站数据库中,如之前在第三章,用户设置和管理页面中所述。

参见

  • 为翻译调整默认用户设置的食谱

修改短代码输出以进行翻译

正如我们在众多食谱中看到的,短代码是强大的工具,为用户提供了一种简单的方法,将内容添加到他们的网站帖子中。由于这些内容是展示给用户的,因此它们可以从翻译中受益,就像网站的管理页面一样。

这个食谱展示了如何在显示之前翻译短代码输出。它还解释了如何处理在不同语言中可以放置在不同位置的可变数据元素。

准备工作

您应该已经遵循了制作管理页面代码准备翻译的食谱,以便为本食谱提供一个起点。或者,您可以从代码包中获取该食谱的结果代码(第十一章/ch11-hello-world/ch11-hello-world-v2.php)。在开始之前,您应该将文件重命名为ch11-hello-world.php

如何操作...

  1. 导航到您开发安装的 WordPress 插件目录中的ch11-hello-world文件夹。

  2. 在文本编辑器中打开ch11-hello-world.php文件。

  3. 在文件末尾添加以下代码行以声明一个新短代码,该短代码将可供内容作者使用:

add_shortcode( 'hello-world', 'ch11hw_hello_world_shortcode' ); 
  1. 添加以下代码部分以提供ch11hw_hello_world_shortcode函数的实现:
function ch11hw_hello_world_shortcode() { 
    $options = get_option( 'ch11hw_options' ); 

    $output = sprintf( __( 'The current text string is: %s.', 
                           'ch11hw_hello_world' ),
                           $options['default_text'] ); 
    return $output; 
} 
  1. 保存并关闭插件文件。

  2. 创建一个新页面(使用页面菜单的“添加项目”),在内容中插入新的短代码[hello-world],然后发布页面(发布)。

  3. 查看页面(查看页面)以查看短代码的输出:

图片

它是如何工作的...

本菜谱展示的内容比前两个菜谱稍微复杂一些,因为我们希望短代码输出是静态文本与动态元素的组合,并且我们希望该元素根据目标语言的语法结构出现在不同的位置。实现此功能的方法是将__国际化函数与sprintf标准 PHP 函数相结合。

sprintf函数的目的是在字符串中插入一个变量。它通过在第一个参数中发送的目标字符串中查找占位符来完成此任务,并用它收到的第二个参数中的变量替换它。一些占位符的例子是%s用于字符串和%d用于整数。考虑到这种功能,我们使用占位符作为要翻译的字符串的一部分,以便创建本地化文件的用户可以选择值在句子结构中的位置。一旦通过__函数获得翻译,我们就可以立即将替代语言字符串发送到sprintf以创建最终文本。

参见

  • 调整默认用户设置以进行翻译 菜单

使用 Poedit 翻译文本字符串

在插入所有必要的代码以查找文本元素的翻译后,我们需要创建实际的翻译文件。虽然有多种工具可用于执行此任务,但我们将集中精力在最受欢迎的一个上,即免费的跨平台 Poedit。

本菜谱展示了如何使用 Poedit 从插件代码中提取所有要翻译的字符串,翻译它们,并将生成的语言文件保存到插件目录下。

准备工作

您应该已经遵循了修改短代码输出以进行翻译菜谱,以便为本菜谱提供一个起点。或者,您可以从代码包中获取该菜谱的结果代码(Chapter 11/ch11-hello-world/ch11-hello-world-v3.php)。在开始之前,您应该将文件重命名为ch11-hello-world.php

如何操作...

  1. 导航到您开发安装的 WordPress 插件目录中的ch11-hello-world文件夹。

  2. 创建一个名为languages的新子目录。

  3. 导航到 Poedit 下载页面,下载适合您计算机的相应版本的工具(www.poedit.net/download)。

  4. 安装并启动 Poedit 应用程序。

  5. 在应用程序的文件菜单下选择“新建”菜单项。

虽然 Poedit 提供了一种自动创建 WordPress 主题或插件翻译的方法,但我们将专注于 Poedit 的免费版本,并突出创建翻译所需的所有步骤。

  1. 选择翻译的语言为法语(加拿大)。

  2. 点击工具栏中的“保存”按钮,导航到本菜谱中先前创建的languages文件夹,并将文件名设置为ch11hw_hello_world-fr_CA.po

  3. 在主界面窗口中点击“从源提取”按钮,打开目录属性窗口,它将默认显示为源路径选项卡。

如果 Poedit 窗口太小,它将不会显示主界面部分。只需将窗口放大,即可看到从源提取按钮。

  1. 通过点击“新建项目”按钮在路径列表中创建一个新的条目。

  2. 将新路径条目的值设置为 ..(两个点字符)。

  3. 切换到源关键词选项卡,并在附加关键词列表中添加两个新项目,并将它们的值设置为 ___e

  4. 切换到翻译属性选项卡,并将项目名称和版本字段设置为 Hello World

  5. 点击“确定”按钮关闭目录属性对话框,然后从工具栏点击“保存”,以便能够开始编辑文件中的条目。

  6. 逐个选择项目,以便在窗口的下方部分显示它们。

  7. 在下方的对话框中为每个文本元素输入翻译。以下截图显示了每个项目的法语翻译:

图片

  1. 完成翻译后,保存翻译文件。

它是如何工作的...

Poedit 工具会搜索 PHP 文件,寻找在关键词配置部分中指定的具有特定名称的函数。它会在与目录本身相同的目录以及目录设置部分中指定的任何附加文件夹中查找所有文件。通过指定 .. 作为附加路径,我们告诉 Poedit 在 languages 文件夹(插件文件所在位置)的上一个目录中查找。

根据我们指定的配置,Poedit 能够找到插件代码中所有 ___e 函数的实例,并检索这些函数的第一个参数中设置的文本字符串。一旦找到所有字符串,Poedit 提供一个简单的界面来为每个字符串提供翻译,并保存生成的翻译文件。保存时,Poedit 实际上创建了两个文件,可移植对象文件和机器对象文件。

语言文件的名称由两部分组成:文本域的名称 ch11hw_hello_world,这是我们之前所有对 ___e 函数的调用中使用的,以及目标语言代码 fr_CA,以匹配我们在本章中之前设置的配置。为了支持 WordPress 支持的所有法语变体,你可以复制翻译文件,每次在 Poedit 目录配置以及文件名(ch11hw_hello_world-fr_CA.poch11hw_hello_world-fr_BE.po)中更改语言代码。

还有更多...

如果你只熟悉英语,创建一个模板文件,用户可以导入以开始他们的翻译。

翻译模板文件

当你只熟悉英语时,你可以创建一个只包含要翻译的文本的翻译模板,通过将目录保存为.pot文件而不是.po/.mo组合来实现。除了特殊的扩展名外,文件名不应包含语言标签(例如,ch11hw_hello_world.pot)。

参见

  • 调整默认用户设置以进行翻译食谱

在插件初始化时加载语言文件

插件翻译的最终步骤是将代码放入加载翻译文件的代码。这是通过注册一个动作钩子回调并在执行时调用一个单一函数来完成的。

本食谱展示了如何加载在先前的食谱中创建的翻译文件。

准备工作

你应该已经遵循了为管理页面代码准备翻译使用 Poedit 翻译文本字符串的食谱,以获得本食谱所需的正确文件。或者,你可以从代码包中获取这些食谱的结果代码(Chapter 11/ch11-hello-world/ch11-hello-world-v3.phpChapter 11/ch11-hello-world/languages文件夹)。你应该将文件ch11-hello-world-v3.php重命名为ch11-hello-world.php,并在开始食谱之前将languages文件夹复制到重命名文件旁边。

如何操作...

  1. 导航到你的开发安装的 WordPress 插件目录中的ch11-hello-world文件夹。

  2. 在文本编辑器中打开ch11-hello-world.php文件。

  3. 在文件末尾添加以下代码行以注册一个在插件初始化时被调用的函数:

add_action( 'init', 'ch11hw_plugin_init' ); 
  1. 添加以下代码段以提供ch11hw_plugin_init函数的实现:
function ch11hw_plugin_init() { 
    load_plugin_textdomain( 'ch11hw_hello_world', false, 
                            dirname( plugin_basename( __FILE__ ) ) 
                            . '/languages' );  
} 
  1. 定位到ch11hw_set_default_options_array函数,并在if语句之前添加相同的load_plugin_textdomain调用:
load_plugin_textdomain( 'ch11hw_hello_world', false, 
                        dirname( plugin_basename( __FILE__ ) ) 
                        . '/languages' ); 
  1. 保存并关闭插件文件。

  2. 导航到“设置”菜单,查看插件菜单项是否已更改。

  3. 选择“Bonjour Monde”项以查看翻译后的配置页面:

图片

它是如何工作的...

load_plugin_textdomain函数有三个参数。当被调用时,它会在最后一个参数指定的文件夹中查找以第一个参数指定的文本域开头的.mo文件,后跟 WordPress 配置文件中设置的当前语言。如果找到,翻译文件将被加载到内存中,并在执行过程中每次遇到___e函数时用于搜索翻译。中间参数设置为false值,已过时但仍然需要向后兼容。

一旦在插件代码中设置了所有钩子,并且提供了一个带有插件的第一个翻译文件(或模板),用户就可以轻松地将文本元素修改为其他语言,他们可以立即使用这些语言。他们还可以将这些新翻译提供给插件作者,以便包含在未来的更新中。我们在激活钩子中添加了对 load_plugin_textdomain 的第二次调用,因为当激活钩子执行时,init 动作钩子不会执行。

还有更多...

随着插件随时间发展,可能需要翻译新的文本项。也可能需要使用更高级的翻译函数并翻译 JavaScript 代码。

更新翻译文件

当在插件中调用 ___e 函数时,需要更新翻译文件以考虑新的文本元素。为此,启动 Poedit 工具并打开现有目录。然后,从工具栏中选择更新。这将提取所有文本项并识别新条目。一旦完成,新项目就可以翻译并保存回目录文件。

高级翻译功能

虽然我们在本章中使用了最常见的国际化函数,但还有一些更高级的函数可能在您的努力中很有用:

  • _n( $singular, $plural, $number, $domain ): 此函数将根据数字是一还是更多来查找接收到的前两个字符串之一。

  • _x( $text, $context, $domain ): 向本地化查找添加一个参数以添加上下文参数。当处理拼写相同但含义不同的单词时,这很有用。

  • _ex( $text, $context, $domain ): 与 _x 相同,但会回显查找的结果。

  • _nx( $singular, $plural, $number, $context, $domain): 与 _n 相同,但增加了 _x 的附加上下文参数。

还有许多函数会立即执行本地化查找,然后转义结果字符串。这些函数包括 esc_attr__(), esc_attr_e(), esc_html__(), esc_html_x() 以及更多。有关国际化函数的完整列表,请访问 codex.wordpress.org/L10n

本地化 JavaScript 文件

JavaScript 文件翻译起来有点棘手,因为它们通常是从不能包含任何 PHP 代码的外部文件中读取的。这个解决方案是 wp_localize_script 函数。当调用时,此函数在已经排队等待加载的脚本中声明新变量,并用本地化字符串填充这些变量。在执行时,代码将能够访问和显示屏幕上的正确文本。以下代码片段是一个示例,展示了如何使用此函数:

wp_enqueue_script( 'script_handle' );
$translation_vars = array( 'string_val' => 
                           __( 'Text to be translated' ) );
wp_localize_script( 'script_handle', 'javascript_object',   
                    $translation_vars );  

在前面的代码示例中,将在 script_handle 脚本内部创建一个名为 javascript_object 的新对象,该对象包含一个名为 string_val 的数据成员,其中包含当前 WordPress 语言中目标文本的翻译(如果可用)。

参见

  • 使用 Poedit 翻译文本字符串的配方

第十二章:在 wordpress.org 上分发您的插件

在本章中,我们将讨论如何分发您的作品,包括以下主题:

  • 为您的插件创建一个 readme 文件

  • 申请将您的插件托管在 wordpress.org 上

  • 使用 Subversion 上传您的插件

  • 为您的插件提供横幅和缩略图图像

简介

一旦您的插件版本准备好向大众分发,您需要决定您是加入官方 WordPress 插件仓库还是自行发布。

在大多数情况下,首选的选项是将您的新扩展添加到官方 WordPress 插件仓库,在那里您有很多好处,包括免费托管、内置的新更新通知以及用户可以在wordpress.org或从他们网站管理页面的插件部分访问的强大搜索引擎。在官方仓库托管的其他好处包括下载统计和创建一个免费论坛,以促进用户支持。为了符合这种托管资格,您的工作必须是免费和开源的,并且必须遵守 GNU 通用公共许可证,版本 2(也称为 GPL v2),这是一种常见的开源软件许可证,WordPress 本身也使用。

这包括您编写的任何代码,以及您在作品中包含的任何第三方 PHP 或 JavaScript 库。要了解更多关于 GPL v2 许可证的信息,请访问www.gnu.org/licenses/gpl-2.0.html。您也可以在官方仓库发布带有付费高级功能的插件,但您不能随着时间的推移将免费功能放在付费墙后面。

相比之下,自行托管让您对定价、分发许可证和作品的一般展示有完全的控制权,但它使得人们更难找到您的插件,并且需要您自己实现定制的更新通知机制,使用第三方库添加等效功能,或者当可用时让用户手动下载更新。还有一些流行的插件数字市场可以帮助缓解这些缺点,但您的工作在官方仓库上的可见性仍然较低。

在将您的插件公开之前,您还应该确保您已准备好处理用户反馈和问题。一旦您的作品可供下载,WordPress 网站管理员会迅速下载、安装它,并可能发现您的工作覆盖了大部分但不是所有他们的需求。当这种情况发生时,您将开始收到添加功能的要求。与用户的这种互动通常是一次很好的体验,可以带来新的想法,从而增强您的工作,但您也应该准备好接受批评,并在修复问题和实现新功能上投入时间。您还需要考虑测试您的扩展程序与 WordPress 新版本的时间,这些新版本通常每年发布两到三次。

本章解释了如何准备您的工作以便上传到官方插件存储库,包括申请账户、使用 Subversion 实际提交代码以及如何自定义插件页面以使其具有独特的外观。

为您的插件创建一个 README 文件

如果您查看官方 WordPress 存储库中的任何插件,您将看到它们的页面包含大量信息,包括扩展的描述、常见问题列表和安装说明。如您所注意到的,我们迄今为止所做的工作,这些数据并不位于主插件的代码文件中。相反,官方 WordPress 存储库在插件中寻找这些信息,这些信息需要包含在一个特别格式化的readme.txt文件中。

本食谱展示了如何为我们在第四章,“自定义帖子类型的威力”中创建的图书评论插件创建一个readme.txt文件。

准备工作

您应该已经遵循了来自第四章,“自定义帖子类型的威力”中的更新页面标题以包含自定义帖子数据使用插件过滤器食谱,以便为本食谱提供一个起点。或者,您可以从代码包中获取该食谱的结果代码(Chapter 4/ch4-book-reviews/ch4-book-reviews-v11.php)。

如何做...

  1. 导航到您的 WordPress 插件目录中的ch4-book-reviews文件夹。

  2. 创建一个名为readme.txt的新文本文件,并在代码编辑器中打开它。

  3. 在文件中插入以下文本:

=== Book Reviews === 
Contributors: ylefebvre 
Donate link: http://ylefebvre.ca/wordpress-plugins/book-reviews 
Tags: book, reviews 
Requires at least: 4.0
Tested up to: 4.8
Stable tag: trunk
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Create your own book review web site! 

== Description == 

This plugin lets you add a book review system to your WordPress site. Using custom post types, administrators will be able to create and edit book reviews to be published on your site. 

== Installation == 

1\. Download the plugin 
1\. Upload the book-reviews folder to your site's wp-content/plugins directory 
1\. Activate the plugin in the WordPress Admin Panel 
1\. Start creating new book reviews! 
1\. Use the [book-review-list] shortcode to list reviews on a page. 

== Changelog == 

= 1.0 = 
* First version of the plugin. 

== Frequently Asked Questions == 

There are currently no FAQs at this time. 

== Screenshots == 

1\. The review edition page 
  1. 保存并关闭文本文件。

  2. 导航到图书评论版页面,并使用第三方屏幕截图工具或操作系统内置功能进行截图。

  3. 将生成的图像保存为screenshot-1.jpg在插件目录中。

它是如何工作的...

readme.txt文件使用类似 wiki 的语法,其中等号(=)的数量表示每个部分标题的级别。最重要的部分是标题,其中包含重要信息,例如插件名称、作者的wordpress.org用户名、捐赠链接、搜索标签、支持的版本,以及对其功能的一行描述。这个最后的项目将始终可见,因为用户在浏览你的插件页面时。

初始标题之后是多个部分,这些部分对应于插件显示页面中的各种部分。更具体地说,这些部分包含扩展功能的完整描述、安装和使用你作品的分步指南、包含所有版本列表及其更改摘要的变更日志、常见问题解答和截图。插件作者还可以使用相同的语法创建他们自己的任意部分。

与标准的 wiki 语法一样,每个安装步骤前面的重复1.在系统使用有序列表在实时网站上显示这些项目时将被转换为递增值。最后,如果readme.txt文件中列出了截图,wordpress.org网站将搜索以关键字screenshot-开头的文件,后面跟着与截图部分中列出的值相对应的数字,并将它们与相关的文本一起作为图例显示。在拍摄插件操作截图时,请确保它们清晰且具有意义,因为访客通常会根据这些图像决定是否下载你的作品。

你可以在插件手册中找到关于readme.txt文件格式的更多详细信息:wordpress.org (developer.wordpress.org/plugins/wordpress-org/how-your-readme-txt-works/)。

还有更多...

为了使插件代码文件更有条理,并对发布有完全的控制,你应该考虑使用 Subversion 标签。

使用标签发布特定的插件版本

标签是 Subversion 的概念,允许开发者在特定时间点识别一组文件,并用名称标记它们。这个名称可以用来指定wordpress.org访客将能够下载的插件版本。虽然这个配方指定了trunk作为Stable Tag的值,表示将发布上传到插件trunk文件夹的最新版本的文件,但在这个字段中可以指定任何其他标签名称。除了使你的工作更有条理之外,使用标签允许你将部分实现的新插件功能提交到你的存储库,而无需它们自动对所有用户可用。

申请在 WordPress.org 上托管您的插件

在为您的创作创建适当的文档之后,将其发布到官方插件存储库的下一步是申请托管。这很简单,只需在wordpress.org的开发者中心部分提交一个请求表单即可。

这个配方展示了如何申请插件托管,并提供了一些快速接受的建议。

如何操作...

  1. 创建一个包含您整个插件目录的 ZIP 存档,包括在先前的配方中创建的readme.txt文件。

  2. 使用插件提交页面顶部的表单,用您的现有凭据登录wordpress.org网站,或者如果您目前没有账户,创建一个新账户。

  3. 将您的网络浏览器指向插件托管请求表单页面,该页面可在wordpress.org/plugins/developers/add/找到。

  4. 选择您的 ZIP 文件并将其上传到网站。

工作原理...

插件提交是一个相对简单的流程,它为开发者提供了访问 Subversion 存储库的权限,他们可以使用它上传他们的作品并与社区分享。

在提交您的插件之前,您应该搜索现有插件以确保您没有选择一个在存储库中已经存在的名称,因为这将导致您的请求被拒绝。您可以通过使用网站的搜索引擎以及尝试访问基于您的插件名称的地址来完成此操作。例如,根据我们的“图书评论”插件示例,您可以检查地址wordpress.org/plugins/book-reviews是否存在。

应该注意的是,当您申请在存储库中列出您的插件时,您的插件需要是可用的。一旦您的插件获得批准,您将获得访问托管 Subversion 版本控制存储库的权限,以便在开发过程中轻松备份所有版本的您的工作,并轻松发布您的工作。如果您与一个人或多个人一起工作在插件上,这个基础设施将使所有贡献者之间的代码交换变得非常容易。

参见

  • 为您的插件创建 README 文件的配方

使用 Subversion 上传您的插件

如果您认为在“第一章”,“准备本地开发环境”的配方中使用 Subversion 是过度杀鸡用牛刀,那么您将看到,一旦您的托管请求被 WordPress 团队批准,这项知识将非常有用,因为系统的后端依赖于这个版本控制系统。

这个配方展示了如何在为您创建存储库后,将您的创作提交到wordpress.org网站。

准备工作

您应该已经遵循了 在 WordPress.org 上托管您的插件的申请 食谱,以在官方网站上获得批准的仓库。您还应该已经安装了 Subversion 客户端,如 第一章准备本地开发环境 食谱中所示。最后,您应该有准备上传的插件文件。

如何操作...

  1. 右键单击文件资源管理器并选择 TortoiseSVN | 仓库浏览器菜单。

  2. 输入您新仓库的地址,如您的托管批准电子邮件中所示。例如,对于名为 Book Reviews 的插件,地址将是 http://plugins.svn.wordpress.org/book-reviews

图片

  1. 右键单击左侧树视图中的插件名称并选择检出选项。

  2. 选择您的计算机上的一个本地文件夹作为检出目录。

  3. 点击“确定”以创建具有伴随版本控制数据的本地服务器结构副本。

  4. 如果结果目录结构为空,将您的插件文件复制到 trunk 文件夹中,或者如果您在提交后继续工作,则使用较新版本更新现有文件。

  5. 如果您添加了任何新文件,右键单击它们,并选择 TortoiseSVN | 添加... 菜单。

  6. 右键单击 trunk 文件夹并选择 SVN 提交... 菜单选项。

  7. 输入一条消息,说明您正在上传新文件到插件仓库以及所做的更改。

  8. 点击“确定”将您的文件上传到官方仓库。

  9. 当提示进行身份验证时,使用您的 wordpress.org 用户名和密码。点击“保存身份验证”复选框以避免每次都提供这些凭据。

  10. 提交文件后,您将收到一封确认邮件,告知文件已上传到仓库,并附带对每个文件所做的更改信息。然后您将能够访问您的插件页面并下载它。对于我们的示例,Book Reviews 插件,页面的地址将是 wordpress.org/plugins/book-reviews

图片

它是如何工作的...

官方的 WordPress 插件仓库使用 Subversion 来管理所有代码文件,为开发者提供版本控制服务,并为扩展页面提供信息。当您的新的仓库创建时,它包含三个主要目录:trunktagsbranches

trunk 目录通常是放置您插件文件最新版本的默认位置。按照食谱中的步骤,我们将文件复制到这个位置并将它们提交到服务器。一旦上传,WordPress.org 服务器将负责创建您工作的压缩副本。

tags目录旨在随着时间的推移保存对您的作品的各个版本的指针,如为您的插件创建 README 文件配方中所述。此功能与您的插件readme.txt文件的稳定标签字段结合使用,允许您在提交和测试可能不稳定的作品到trunk的同时,将用户重定向到已知的工作版本。新标签是通过使用 TortoiseSVN 菜单中的分支/标签项并关联一个名称到特定修订版来创建的。branches目录与tags具有类似的功能,但更专注于创建插件或包含特定功能的开发修订版的替代版本。

更多内容...

如果您想在编写插件代码时在本地 WordPress 开发安装中执行它,以下部分将向您展示如何管理您的代码。

将插件检出到您的开发安装

当检查完整的插件目录时,您最终会得到一个不能直接在 WordPress 本地开发安装中进行测试和开发的结构。而不是检查整个目录结构,您可以限制您的选择到trunk目录。这将只复制该特定文件夹的内容到您的系统,并且您可以设置目标文件夹位于插件目录下。

参见

  • 为您的插件创建 README 文件的配方

  • 在第一章,准备本地开发环境中的从 Subversion 仓库检出文件配方

  • 在第一章,准备本地开发环境中的向 Subversion 仓库提交更改配方

提供插件横幅和缩略图图像

虽然通过创建readme.txt文件并将其上传到官方插件仓库来实现的插件列表是功能齐全的,但它并没有在网站上众多扩展程序中脱颖而出。幸运的是,wordpress.org引入了一种机制,允许插件开发者向他们的列表添加横幅图片。这张图片可以是任何从简单图片到复杂图形,以宣传您的作品。

此配方解释了如何为您的插件准备图像以及如何将它们上传到您的仓库。

准备工作

您应该已经遵循了申请在 wordpress.org 托管您的插件使用 Subversion 上传您的插件配方,以便在官方仓库中有一个批准的仓库,并将插件文件上传到服务器。

如何操作...

  1. 创建一个精确为 772 x 250 像素的新图像。

  2. 将图像保存为名为banner-772x250.png的 PNG 文件。

  3. 右键单击文件资源管理器并选择 TortoiseSVN | Repo-Browser 菜单。

  4. 输入您的插件仓库地址。例如,对于一个名为 Book Reviews 的插件,地址将是http://plugins.svn.wordpress.org/book-reviews

  5. 在与trunktagsbranches相同的级别创建一个名为assets的新顶级目录。

  6. 选择assets目录,然后将新图像文件拖放到文件夹中上传到服务器。

  7. 在弹出的对话框中指定一个日志消息,以解释为什么文件正在上传。

  8. 创建大小正好为 256 x 256 像素和 128 x 128 像素的小图像,并将文件分别命名为icon-256x256.pngicon-128x128.png。较大的图标应该是较小图标内容的更高分辨率版本,因为它将在高 DPI 显示上替换较小的图标。

  9. 重复步骤 6 和 7,将两个图标上传到assets目录。

  10. 访问您插件在wordpress.org上的页面,查看图像是否正确显示:

图片

  1. 通过wordpress.org/plugins页面或 WordPress 插件管理界面的“添加新插件”部分搜索您的插件,以查看图标和简短插件描述的使用方式:

图片

它是如何工作的...

当文件上传到插件仓库时,wordpress.org网站会检查是否存在特定名称的特定图像文件,用于插件横幅和图标。如果这些文件存在,它将更改插件页面的布局以包含这些图像。在创建插件横幅和图标时,尊重图像格式和指定尺寸非常重要,以确保它们在网站上正确显示。

参见

  • 使用 Subversion 上传您的插件的步骤
posted @ 2025-09-09 11:30  绝不原创的飞龙  阅读(33)  评论(0)    收藏  举报