精华内容
参与话题
问答
  • Drupal8的详细建站教程

    千次阅读 2017-05-08 15:35:59
    什么是drupal?  drupal是一个好用且功能强大的内容管理系统(CMS),通常也被称为是内容管理框架(CMF),由来自全世界各地的开发人员共同开发和维护,目前最新版本是Drupal 8。 安装drupal所需基础  建站...

    什么是drupal?

          drupal是一个好用且功能强大的内容管理系统(CMS),通常也被称为是内容管理框架(CMF),由来自全世界各地的开发人员共同开发和维护,目前最新版本是Drupal 8。

    安装drupal所需基础

          建站环境:Windows或Linux操作系统
         Web服务器:Apache,Nginx,Lighttpd,或微软的IIS服务器,只要能支持PHP环境的web服务器都可以
         关联的数据库:MySQL或PostgreSQL都可以
          PHP:Drupal 8 要求的PHP版本為5.5.9或更高的版本
    注:如果要查看Drupal详细的环境需求,可以查看Drupal官网的System requirements

    开始安装

           笔者的操作系统是Windows,本次笔者将使用一个叫XAMPP的集成软件包(XAMPP表示Apache,MySQL,PHP,Python),它的安装和配置对于最初级的技术人员来说都是很简单的,至于它的安装,网上都有很多教程的,这里笔者就不做介绍了。

    1、下载Drupal 

    1)连接drupal官网的drupal下载页面  ,可以看到不同版本的Drupal Core,选择下图所示的最新版本的Drupal 8。

      

    2)只要从Drupal官网上下载下来的资源,都可以看到有两种格式可以选择,一般来说,如果您使用Windows系统来建设Drupal网站,那会选择“ZIP”的压缩格式;如果使用Linux系统,那么“tar.gz”和“zip”两种压缩格式都可以选择,如下图所示:



    3)将刚刚下载好的Drupal 8安装包解压,并上传至您网站的主机空间或将其移至您电脑本机存放网页的根目录,本次笔者是在本地搭建,故需把解压后的Drupal 8移至 根目录,如下图所示:



    4)移至完成后,需要在你刚才解压的drupal文件中的sites目录下的default文件中将default.services.yml和default.settings.php文档各自复制一份,并把复制后的文档命名为services.yml 和settings.php,具体如下图:



    5)在sites/default目录下创建一个叫 files的文件夹,用作储存你网站上传的文件和资料。

    2、安装Drupal 8

    1)新增与配置资料库:点击Xampp中MYSQL中的Admin,然后登陆phpMyAdmin(第一次登陆使用者名称为 root,密码为空,然后直接点执行),创建新的数据库,如下所示:



    2)为你刚才创建的数据库新增一位使用者,具体步骤如下:



    3)配置drupal网站
    ①浏览器中输入http://localhost/你所命名的资料夹或网页名称,当然localhost也可替换成127.0.0.1,都是本地的意思,如下图笔者的资料夹是命名为 drupal8,所以网址输入是http://localhost/drupal8:


    ②在语言选项中选择English,或选择简体中文,当然如果你选择中文,这就需要去官网下载drual语言翻译下载页面,在这里笔者是选择English,当然我的英语水平也很菜,由于后续要示例开发多语言网站,故笔者选的是English;下一步后选择 页面中的 Standdrd,也就是标准:



    下一步后会出现如下页面:



    其中出现的黄色警告讯息,是PHP OPcode caching未啟用的問題,这问题解决的方法很简单,只需修改PHP設定檔,將OPcache的快取功能啟用就可以了,如下:
       在xampp文件夹下的php文件中找到php.ini,然后打开, 找到 opcache.enable=0,将它前面的注释去掉,并将其值改为 1,然后在下面增加一行:zend_extension=php_opcache.dll 就可以了,记得保存后要重新启动Apache服务器。

       

    ④填写信息:资料库就填写你之前新增的资料库,使用者名称就填写你之前新增的使用者名称及密码,如下:



    ⑤然后就是安装,安装后会出现网站配置的页面,其中 依次输入你的网站名称及网站电子邮箱(这两个后面都可以更改的),再设定你网站的超级管理员,这个账号的权限是最大的,不受任何限制,也就是我们所谓的超级使用者,一般来说,在管理drupal网站时,不建议直接使用超级使用者账户去管理网站;最后依次输入电子邮箱地址,及国家地区的选择。




    ⑥点击 保存继续,出现如下页面,这样,我们的基本网站就搭建成功了,后续再在网站上做进一步建设就可以了。

    展开全文
  • 学习drupal过程中看到的几本不错的电子书和资料,免费分享给大家,有需要的可以看下。对drupal的提升很有帮助
  • Drupal开发学习入门指引

    千次阅读 2019-01-30 09:31:18
    我是云客,《云客drupal8源码分析》系列的作者,伴随着drupal8第一个正式版本发布到现在,该系列已经发布100期,超过60万字,作为中国投入精力最大、同时也是时间最早的第一批用户之一,云客觉得有义务帮助初学者...

    我是云客,《云客drupal源码分析》系列的作者,伴随着drupal8第一个正式版本发布到现在,该系列已经发布100期,超过100万字,作为中国投入精力最大、同时也是时间最早的第一批用户之一,云客觉得有义务帮助初学者准备好行囊,叮嘱好一路上的注意事项,之前也零星写过一些入门方面的资料,会在本文末给出链接(这些资料也很值得参考),本篇将为您全面整理需要的一切和规划学习步骤,这里假定您是一个刚刚学会php正在寻找下一步方向的开发者。

    首先是语言问题,drupal是国际协作的开源软件,以英语作为主要沟通语言,因此很多第一手资料是英语的,但不用担心,一是现在中文资料已经有很多了,二是在官网文档和源代码注释里面使用的英语都非常简单,建议您安装有道词典,该软件可以划词翻译,很方便,初中水平的英语基础在翻译软件的帮助下,学习应该不成问题,不要被英语阻碍了您的步伐,事后会发现其实这没什么

    然后是搭建本地开发环境,假定您使用win操作系统,这里推荐您使用phpwamp或者phpStudy集成套件,这两个软件都很优秀,它们集成了多个php版本、主流的三种服务器软件、多个mysql版本等等,可以随意切换, 经过简单的配置和修改本地的hosts文件就可以在本地安装多个任意域名的站点,对于开发而言非常方便

    再选择一款优秀的集成开发工具(IDE),这里推荐您使用phpstorm,这是一款非常优秀聪明的开发工具,智能化水平非常高,在新建项目时内置了以drupal模块方式新建,这在开发上带来极大的辅助,这里列出一些最常用的操作和快捷键:
    打开drupal类文件:“Ctrl+N”,粘贴类的完全限定名称,回车即可,因为drupal8系统使用PSR-0和PSR-4标准的类自动加载器,phpstorm会依据这自动打开,因此您不需要在文件系统里面去找,非常方便,此外对着类名点击鼠标中键可以起到同样的作用,也正因为drupal使用自动加载机制,所以在很多资料里面在谈及某个类文件时,只给出全限定类名,而不会提到类文件的路径,有经验的开发人员或IDE能够通过类名判断出文件所在位置
    格式化代码:“Ctrl+Shift+Alt+L”打开格式化代码对话框
    添加删除注释:行注释“Ctrl+/”,块注释“Ctrl+Shift+/”,删除时再按一次即可
    在某路径下所有文件中查找某字符:“Ctrl+Shift+F”,这非常有用,可以用来查找钩子或函数在哪些地方被使用,关于该功能另外一个软件Sublime也很不错
    查看一个类或接口在哪些地方被使用了:光标定位到类名,再点击“Ctrl+B”或“Ctrl+ Alt+B”
    云客通常使用4种开发工具,phpstorm是最喜欢的,她还有很多非常棒的功能,这里不一一例举了

    在drupal里面有些变量非常巨大,如果您采用print_r($var);或var_dump($var);的方式查看,即便有16GB的内存,系统也会被耗尽卡死,因此您需要使用断点调试工具,关于这请查看云客的专门介绍:《php调试工具Xdebug使用教程》,地址:
    https://blog.csdn.net/u011474028/article/details/79571909

    开发所需的环境和工具准备好了,我们来谈一谈学习步骤:
    首先您需要知道一些基本的drupal概念,比如:实体、插件、释文、钩子、主题、区块等等,因此建议您花一周的时间去官网浏览一些基本文档,同时也熟悉官网的结构,浏览完后您依然会对一些概念不甚理解,但没有关系,有一个初步的印象足够了,接下来去查看中文资料,对一些疑惑的地方做进一步的了解,以下这些网站都很不错:
      爱码文档汇:http://www.nowicode.com/
      drupal中国:http://drupalchina.cn/
      drupal大学:http://www.drupal001.net/
    还有很多,不一一列举了,在浏览这些网站的时候不要对一些高深的内容惦念不放,这一阶段的主要目的是熟悉基本概念、社区、资料来源等等,这会为以后的学习打下基础
    接下来您需要一本简单的系统性介绍drupal的中文书籍,推荐《Beginning Drupal 8》,地址:
         http://drupalchina.cn/node/6096
    该书着重讲解如何使用drupal8,篇幅不多,但较为系统的让您融会贯通了一次,有了一次完整的使用体验,此时您应该可以在使用层面获得一些成就感,对drupal的强大有更加深入的了解了,此时请您在心里感谢一下那些参与该书翻译的社区成员,龙马组织大家花费了很多的时间,云客有幸也参与了其中一章的翻译,感受到社区的力量和良好氛围,各类翻译工作还在继续,有朝一日您成为过来人也请不要忘记辅助正在入门的新人。

    现在您的基础已经足够了,可以开始学习开发,首先您需要对整个系统的主要结构有所了解,drupal是典型的结构简单,但细节繁多,这里需要告诉您一句话:任何web系统都是将请求转化为响应的系统。这是一个非常朴实但博大的理念,这是由symfony框架提出的,symfony在这个理念基础上设计了系统基本流程,详细请参考:
      https://symfony.com/doc/3.4/components/http_kernel.html#the-workflow-of-a-request
    drupal8采用了symfony框架的部分组件,因此drupal也是基于这个流程的,这是系统的核心骨架,因此该链接应是您学习开发时详细阅读的第一篇资料。

    关于drupal和symfony的关系,需要在这里进行一下说明,有一些资料说drupal8是基于symfony的,这里要告诉您这不能说错,但并不恰当,准确的说法是drupal采用了symfony的一些组件,在代码量上symfony占比是非常小的,而且采用的组件中非常多的地方经过了继承修改,如果说是“基于”,那么容易误导学习者,让人误以为必须要先学习symfony才能开始学习drupal,而事实上完全没有必要,当然如果您已经学习过symfony那么对drupal的学习会很有帮助,没有学过也没关系,您可以直接开始学习drupal。说是 “基于”还带来一个误解,一些评论人员会说symfony的bug会影响到drupal,使得drupal不安全,更有甚者说drupal会受制于symfony,其实都是庸人自扰,他们太过高估了symfony的代码占比,又低估了drupal对symfony的继承修改,云客在此想告诉您:事物的发展是递进的,依次建立在前者的基础上,对前者的担心没有必要,就像我们做web开发不会去担心操作系统或者硬件是不是足够好一样。

    在了解了核心流程骨架后您可以从系统的入口index.php文件开始看一看,接下来马上就是对composer运用的学习,她负责加载系统运行中的所有类文件,此时您可以开始阅读《云客drupal8源码分析》系列了,从第一篇开始阅读,此系列是依据系统执行流程发布的,跟您的探索步骤相吻合,虽然取名为源码分析,实则也是开发者教程,是云客从不知道变为知道的过程中留下的资料,非常注重学习者的心理状态,在您的学习路上答疑解惑,drupal很庞大,这个过程会很漫长,云客将在此系列的字里行间常伴您左右,直到您可以成为一名drupal核心贡献人员,这个过程中您仅需要的是:坚持,祝您心有所成,坚持并快乐着。

    云客为您准备了一个学习模块:yunke_help模块,下载链接:
       https://blog.csdn.net/u011474028/article/details/80888152
    该模块专门为drupal8学习者打造,通过她您可以查看到系统的许多数据,也提供了一些常用操作,不管是学习还是正式的开发,都带来极大的方便,如果您需要一些开发方面的示例代码,可以下载官网的示例模块,下载地址:
       https://www.drupal.org/project/examples
    更多的资源链接已经集成到了yunke_help模块,请下载查看

    最后讲一讲drupal大版本的区别和联系:
    在drupal7时,系统是用过程式代码开发的,伴随php语言本身的发展,drupal8实现了一次大飞跃,采用面向对象方式开发(oop),重写了几乎所有代码,因此对于学习而言,版本8是一个新的切入点,您不需要去专门学习D7版本;发展是递进的,D8虽然代码重写了,但很多思想和概念是历史版本的沉淀和延续,如您有兴趣可以多去了解D7版本,如果您学习过D7那么学习D8会容易许多;drupal9计划在2020年推出,但您不必担心学习过时,D9是在D8基础上平滑升级,变化远远没有D7到D8那么大,在云客看来对于开发者的影响会非常小,因为已经跨入了OOP方式,极大部分代码是不需要变化的,所学内容会被自然延用。

    再过几天,将迎来除夕,欢度春节,祝读者新年快乐!

    相关链接:
    《drupal心得:难的价值、未来趋势、拥抱王者》(写于2018年):
    https://blog.csdn.net/u011474028/article/details/86641647

    《为什么选择drupal?为什么做贡献?怎么学?怎么贡献?》(写于2017年):
    https://blog.csdn.net/u011474028/article/details/74295701

    《drupal 8 入门》 (写于2016年):
    https://blog.csdn.net/u011474028/article/details/52514472

    《云客drupal8源码分析系列》(首发于作者博客和爱码文档汇):
    https://blog.csdn.net/u011474028
    http://www.nowicode.com/

     

    我是云客,【云游天下,做客四方】,联系方式见主页,欢迎转载,但须注明出处

     

     

     

    展开全文
  • Drupal 初次使用感受,后续补充。

    千次阅读 2014-06-18 08:56:12
    最近重新想到开源CMS,好奇看到那么多人推崇drupal,也便下载来重新研究了下。 刚接触了下,不过整体使用感觉很差,尤其几个地方: 1)本地安装都非常缓慢,途中还提示执行超时。 可想而知,如果放到网上去,会...

    很久以前就接触过,下载下来安装,结果界面太丑,太难看,直接删除。

    最近重新想到开源CMS,好奇看到那么多人推崇drupal,也便下载来重新研究了下。

    刚接触了下,不过整体使用感觉很差,尤其几个地方:


    1)本地安装都非常缓慢,途中还提示执行超时。

    可想而知,如果放到网上去,会怎么样的卡资源?空间提供者非封了网站不可。


    2)数据库表没有前缀。

    这多都多少年代了,数据库表还是直接一次性安装连前缀都不给了。对比中国的那些CMS 开源产品真的是太落伍了。


    3)数据库设计非常耗费资料。

    看了一些网上对drupal的博文,描述得神乎其神,尤其是提到drupal是一款CMF,内容管理框架(一种万能的感觉),也就是可以用drupal来开发任何想要的程序。

    万能的?看了数据库结构,确实可以开发任何东西,因为他一个文章都分了N个表来存储,但是问题是,这样的运行对服务器配置要求相当高,查询一篇文章都得查询多个表。

    想象一下,像国产dedecms这类都是MyISAM,直接2个表查询。他这样的设计模式,drupal这样的设计模式非常耗费资源。


    4)不支持生成静态

    国外的那些CMS,压根就不支持静态。而是一种插件化的思路。他们的思路都是一种拓展方式。但是安装的插件/拓展越多,就会越卡。这对于中国的使用者来说,太奢侈。

    就好比国外的软件现在基本上都是在线安装,几百MB的都在线安装。chrome也玩在线安装,结果很多人根本安装不了,问题在于大部分中国人的网速根本1M都没有,30K~50K就不错了。国情~ 国外的人基本上都是用VPS,国内的大部分都是虚拟主机,几百个网站同一个服务器上。如果不支持生成静态,每次都直接查询数据库,只会非常卡,且无法承受多少访问量。


    5)官网几乎打不开,下载更是很久

    drupal的官网都很难打开,我是使用的goagent代理才打开的。下载drupal总是失败,后来用迅雷才下载成功,估计是从迅雷分享资源里面的,而不是从drupal直接地址下载来


    (后续补充 default7#zbphp.com)



    ----------------------------------

    2014-6-29 补充:

    drupal是支持自己设置表前缀的,在安装的时候advanced setting里面,设置table prefix。





    展开全文
  • drupal模块开发指南

    2015-10-23 11:58:57
    它将告诉您如何更改Drupal的设置,使drupal显示错误信息。 为您的模块命名  建立模块的第一步是为它取一个不太长的名字。这个名字将被用在所有的模块文件名和方法名中。所以这个名字必须以字母开头并只能有小写...

    开始之前

          如果您需要用PHP来查出您站点上的错误,请访问此链接 (link is external)。 它将告诉您如何更改Drupal的设置,使drupal显示错误信息。

    为您的模块命名

           建立模块的第一步是为它取一个不太长的名字。这个名字将被用在所有的模块文件名和方法名中。所以这个名字必须以字母开头并只能有小写字母和下划线。比如,我们会用"current_posts"作为一个模块名称。

          注意:一定要确保根据以上规则来取名,因为它将被用于模块名和方法名的前缀。当您运行drupal“钩子“时(请参看后面的章节),drupal将只能识别拥有与您模块文件名称相同前缀的钩子。

    请注意不要使用与您站点所有主题相同的名称,这样会使drupal混淆。

    创建一个模块目录和模块文件

           假如我们决定用"current_posts“的模块名,我们将会在drupal的根目录的以下路径(sites/all/modules /current_posts)创建一个目录。 或者如果您要把自己的自定义模块与其他模块区分开,您也可以选择这个路径(sites/all/modules/custom /current_posts)。然后在sites/all/modules/currents_posts目录创建一个名为 current_posts.module的文件。注意 drupal并不识别扩展名为.php的文件。如果您为模块添加了php的扩展名,那将不会被识别。drupal只识别扩展名为.module的php文 件。

           在Drupal6中,sites/all/modules/是存放自定义模块的理想目录(sites/all/themes/是存放自定义主题的理想目录),因为这个目录存放这所有只与您的站点相关的模块和主题,在您以后升级您的核心模块时,这些自定义模块和主题不会被改写。或者,如果您有多个子站点共享核心模块,而这个自定义模块只在其中一个 站点工作,那么您可以使用这个路径来存放: sites/您的子站点/modules

    模块文件的开头起始于php的起始标签 (link is external)“<?php". 不要把cvs版本控制系统的标签放到模块文件中,drupal应用git的使用规范来实现版本控制。如果coder模块显示错误信息,说明此模块还没有被升级,并不符合drupal的git规范。

    我们的模块还不能被执行,它还没有被激活。稍后我们将激活这个模块。

    编码标准

           根据drupal编码标准 (link is external), 我们在模块中省略"?>"。否则我们可能会遇到服务器实时运行错误。(注意,在本教程中,我们的示例代码中包括了“?>", 这只是为了使代码显示的更好。请在实际应用中忽略"?>")

    所有以{模块名}_{方法名}格式命名的方法,都是钩子,都可以被drupal直接调用。其中{方法名}是预定义的方法名后缀。drupal将会调用这些方法来处理数据。所以取一个简单明了的模块名是十分重要的,drupal会知道去哪查找数据。

    下一步,我们将踏入钩子的领域。

    所有的Drupal模块都有一个"模块名.info"文件,用来储存该模块的元信息。(其实可以理解成模块的描述文件)

    该文件的一般格式为:

    name = Module Name

    description = A description of what your module does.

    core = 7.x

     

    在本示例中,我们用我们自己的模块名"current_posts"来代替上面例子中的"ModuleName". 如果没有.info文件,drupal不会将我们的模块添加进模块列表。下面就是修改后的.info文件内容:

     

    name = Current Posts

    description = A block module that lists links to recent posts.

    core = 7.x

    我们把以上内容添加到一个名为current_posts.info的文件中,并把该文件保存在模块的根目录sites/all/modules/current_posts下。

    注意:如果您复制以上代码并粘贴到.info文件, 留意description的内容不要有空行,否则本文件将不会正确的读取。

     

    .Info文件的细节

     

    drupal把所有能够放到.info文件的参数都放到了这个网页 (link is external), 请参看。

     

    检查

    我们现在有了.info文件,要想从drupal文件列表里找到我们的模块,我们只需要一个空的.module文件就可以了。只需要在同样的模块目 录下建 立一个.module文件,并在.module文件里添加<?php,我们就会发现我们的模块已经在drupal的模块列表里了。

    Drupal模块中的注释语言

    在您的模块中添加注释来解释您的模块如何工作,是个很好的习惯。我们需要严格的遵从drupal的注释规则来发布补丁或新模块, 因为drupal用Doxygen来从代码的注释语言中生成文档。请查询doxygen和注释规范 (link is external)来获得更多相关信息。接下来的教程对大家是非常有帮助的,即使您也许目前并不需要它。

    下面是我们的第一个注释:

    <?php
    /**
     * @file
     * A block module that displays recent blog and forum posts.
     */
    ?>

    @file告诉您此注释适用与整个文件。注释起始于"/**", 结束于"*/"。按照drupal指南所要求的,drupal模块 (link is external)中所有的方法都将用这个注释格式。

    运行我们的第一个钩子

           钩子是drupal模块的基本。它们让我们把自己的模块整合到drupal核心模块中去。如果你不知到这些,请参看drupal模块的入门 (link is external)

           我们的第一个模块是hook_help. drupal推荐我们在所有模块中应用这个钩子。hook_help为用户提供此模块的帮助信息。要在drupal中运行钩子, 我们要把"hook"替换成我们模块的短名,并在module文件中用同样的名字建立一个方法。所以,要在我们的示例模块中运行hook_help,我们应该在 current_posts.module文件中建立一个叫做current_post_help()的方法:

    <?php
    function current_posts_help($path, $arg) {

    }
    ?>

    $path变量提供了相关帮助信息的参数:在durpal或模块的什么地方,我们的用户会需要帮助信息。要想处理这个参数的公认的好方法是用 switch语句。 这是drupal非常流行的编写模式。我们的模块将采用下面这个简短的钩子示例,我们还可以添加注释语言在里面。

    <?php
    /**
     * Implements hook_help.
     *
     * Displays help and module information.
     *
     * @param path
     *   Which path of the site we're using to display help
     * @param arg
     *   Array that holds the current path as returned from arg()function
     */
    function current_posts_help($path, $arg) {
      switch ($path) {
        case "admin/help#current_posts":
          return '<p>'.  t("Displays linksto nodes created on this date") .'</p>';
          break;
      }
    }
    ?>

    注意我们不应该有"?>"

    drupal核心模块会把“admin/help#模块名"链接到drupal的帮助页面"/admin/help/或者 ?q=admin/help"。 我们最终将会添加更多的注释语言, 请参看注释语言标准 (link is external)。这个方法不仅允许第三方对它的翻译,更加加强了代码的安全性。请参看LocalizationAPI (link is external)来获得更多的信息。

    检查

           在工具栏点击"模块", 我们便可以在模块列表找到我们的模块。激活模块并点击保存,刷新本页面,您将看到我们的模块旁边有了一个"help"链接。 点击这个链接,我们就能看见current_post_help钩子返回的内容了。或者您可以在工具栏点击帮助链接(http://example.com/admin/help(link is external)), 您将看见我们的帮助链接被列在帮助列表上。

    卸载我们的模块并点击保存。(注意:卸载我们当前未完成的模块是很重要的,我们未完成的模块很容易破坏我们的站点。

    声明区块

          模块可以被用来做各种事情, 比如:创建区块(一段简短的内容,通常出现在页面的左右两边);建立特殊的内容类型(就像您现在正在阅读的内容);查询后台信息等等。您也许听说过 block模块--专门被用来创建区块内容(比如菜单),或者node模块--专门用来生成页面的内容(比如日志和论坛)。在我们的例子中,我们所建立的是一种block模块,因为我们创建了一个区块(block)。

    drupal7 (link isexternal)提供了至少8个block钩子可以应用。根据我们例子的需求,我们将用到其中的两个。第一个钩子是hook_block_info()。您可能已经猜到了,这个钩子用来告诉Drupal我们要建立的区块的相关信息。我们将用这个钩子来定义一个能够显示最近新提交的内容的区块。您可以使用任意一个模块提供的block钩子,如此您将需要定义相关模块所需的所有区块,在我们的例子中,我们只需要定义一个区块。

    要定义我们自己的区块,我们需要打开current_posts.module文件,并建立一个叫做current_posts_block_info方法,如下

    <?php
    /**
     * Implements hook_block_info().
     */
    function current_posts_block_info() {
      $blocks['current_posts'] = array(
        'info' => t('Current posts'), //The name that will appearin the block list.
        'cache' => DRUPAL_CACHE_PER_ROLE, //Default
      );
      return $blocks;
    }
    ?>

    注意在我们真正的代码中不应该有"?>"在结尾。

    代码中的注释已经把这个钩子解释的很充分了。大家可以访问hook_block_info的API页面或者运行一个hook_block_info钩子来了解更多。

    我们将把整个队列返回到drupal中。 请注意例子中队列的格式和结构,只是drupal指定的队列结构。队列在PHP中被支持和运行的很流畅,drupal沿用并扩展了队列的用途。

    在我们的例子中,唯一一个必须的队列元素是info, 但是hook_block_info还能够把其他的设置具体化。比如在这个里,我们把缓冲存储设置为默认。请参看hook_block_info (link is external)来获得完整的可用的元素信息。

    别忘了返回整个队列到drupal

    检查

           访问模块页面并激活Current Posts模块,保存设置。下一步,访问Structure(结构)->Blocks(区块)页面。 在页面的底部,我们应该能够找到被命名为"Current posts"的区块。如果能够找到,说明我们的钩子安装成功了。下一步我们就可以卸载我们的模块并保存设置了。注意:我们必须卸载未完成的模块,因为未完成的代码可能会导致我们的站点无法工作。

    获取信息

           下一步我们将建立一个自定义方法来获取最近在站点发布的内容。当一个节点被建立时,Drupal把它的建立时间存储到数据库中。我们将用这个数据库字段来查询我们所需的信息。我们本可以把这段代码放到我们下一章要讲的钩子中去,但是把这段代码单独放到一个方法里能够是我们的代码更加清晰明了,便于阅读和管理。

    我们将称这个方法为current_posts_contents。我们继续遵循将模块短名作为方法名的前缀的规则,跟着我们放一个非drupal钩子名的词在模块短名后面。这个方法起始于如何获得当前时间。下面是代码的第一部分:

    <?php
    /**
     * Custom content function.
     *
     * Set beginning and end dates, retrieve posts from database
     * saved in that time period.
     *
     * @return
     *   A result set of the targeted posts.
     */
    function current_posts_contents(){
      //Get today's date.
      $today = getdate();
      //Calculate the date a week ago.
      $start_time = mktime(0, 0, 0,$today['mon'],($today['mday'] - 7),$today['year']);
      //Get all posts from one week ago to the present.
      $end_time = time();
    ?>

    这段代码用来获得当前时间参数(自从新纪元以来到现在的秒数,请参考mktime (link is external)来了解这里面的含义和格式),然后我们便可以计算出一周前午夜的时间参数。这段代码是PHP语言编写的,您可以访问php.net (link isexternal)来获得更多信息。

    下一步我们将用drupal的数据库API来获取我们的节点列表。这是我们自定义方法的第二部分。

    <?php
      //Use Database API to retrieve current posts.
      $query = db_select('node', 'n')
        ->fields('n', array('nid', 'title', 'created'))
        ->condition('status', 1) //Published.
        ->condition('created', array($start_time, $end_time),'BETWEEN')
        ->orderBy('created', 'DESC') //Most recent first.
        ->execute();
      return $query; 
    }
    ?>

    drupal有自带的非常健全的数据库语言构造器,它运用了特有的面向对象API。我们要注意INSERT,UPDATE,DELETE语句将使用以上的格式来构造。drupal将把这个对象转换为相应数据库语言。对于SELECT语句,虽然我们并不是必须要使用以上格式,但是使用以上格式会加深我们对drupal数据库构语句造器的理解。我们在drupal中见到很多类似的结构.

    1. 我们用db_select命令来构造一个SQL语句,我们将需要把数据库表名和它的简写作为参数传给这个命令。
    2. 这里的field命令用来告诉SQL我们要从那个表(第一个参数)查询那些字段信息(第二个参数中的队列)。
    3. condition命令需要三个参数。第一个参数是要查询的字段。第二个参数数我们要查询的值。第三个参数是比较的条件。如果第三个参数是空,那drupal将使用默认条件--等于“=”。
    4. order命令将根据传进去第一个参数将查询结果排序,第二个参数决定是升序还是降序。
    5. execute命令用来编译和执行我们所构造的SQL语句,并返回结果(一个对象)。

    以下是最终的代码:

    <?php
    /**
     * Custom content function.
     *
     * Set beginning and end dates, retrieve posts from database
     * saved in that time period.
     *
     * @return
     *   A result set of the targeted posts.
     */
    function current_posts_contents(){
      //Get today's date.
      $today = getdate();
      //Calculate the date a week ago.
      $start_time = mktime(0, 0, 0,$today['mon'],($today['mday'] - 7),$today['year']);
      //Get all posts from one week ago to the present.
      $end_time = time();

      //Use Database API to retrieve current posts.
      $query = db_select('node', 'n')
        ->fields('n', array('nid', 'title', 'created'))
        ->condition('status', 1) //Published.
        ->condition('created', array($start_time, $end_time),'BETWEEN')
        ->orderBy('created', 'DESC') //Most recent first.
        ->execute();
      return $query; 
    }
    ?>

    请记住在我们的代码中不要包含"?>"

    生成区块内容

    访问权限检查

    这个是代码的第一部分

    <?php
    function current_posts_block_view($delta = '') {
      switch($delta){
        case 'current_posts':
          $block['subject'] = t('Current posts');
          if(user_access('access content')){
           //Retrieve and process data here.
       }
    ?>

    在把信息显示给用户之前,我们要检查当前的用户是否有权限来查看此信息。在这里,我们执行最基本的权限检查“accesscontent“。 您也可以用Drupal所提供的其他的权限检查,或者建立您自己的权限。要查看drupal权限列表,请访问people>list(或者http://example.com/admin/people). 在权限的下拉列表中,您将看到您可以使用的权限的机器名。这个列表将包含您站点的所有权限,包括任何自定义模块的权限。那permission页面的权限 名称并不是机器名,所以您不可以把它们用到代码里。

    查看可用权限的另一个方法是参看API参考 (link is external)。 输入模块名并加上_permission的后缀,比如node_permission。 我们将看到node定义的所有权限。要想知道哪一个权限才是我们需要的没那么容易,比如access_content是我们在这里要用的,但是它不是block模块所提供的,而是node模块提供的。

    在代码中建立链接

    这里是下一步的代码

    <?php
           //Use our custom function to retrievedata.
            $result = current_posts_contents();
            //Array to contain items for theblock to render.
            $items = array();
            //Iterate over the resultset andformat as links.
            foreach ($result as $node){
              $items[] = array(
                'data' =>l($node->title, 'node/' . $node->nid),
              );
      }
    ?>

    首先,我们在我们自定义的方法中,把内容存储到一个叫作$result的变量中,然后用$item变量来存放要处理的信息。在循环中,我们访问每一个节点,然后根据节点的信息来构建一个链接。

    注意,链接是被l()方法所建立的。(小写的L)。第一个参数定义了链接的名字,在这里是节点的名称。第二个参数定义了链接的路径。所有通向节点的路径总会是像"node/#", "#"是节点的ID。l()用这个路径来生成真正的链接。把url转化成相对drupal安装 (link is external)目录的URL

    主题化我们的信息

    drupal的表示层,也被人们熟知为主题层,是一个可以兼容不同主题的系统。每一个主题都可以控制drupal的外观,并且可以控制CSS文件。Drupal主题 (link is external)是一个很大的方面,我们甚至可以用一整本书来讨论它。在这里我们只用到了drupal主题的皮毛。我们将会研究主题是如何分析解读队列的。

    这里是 current_posts_block_view 方法的最后一部分。

    <?php
            if (empty($items)) { //No content inthe last week.
              $block['content'] =t('No posts available.'); 
            } else {
              //Pass data throughtheme function.
              $block['content'] =theme('item_list', array(
                'items'=> $items));
            }
          }
      }
      return $block;
    }
    ?>

    首先,我们要考虑一种可能性,就是我们可能没有任何内容符合要求。我们的区块应该显示是否有上周新发布的内容。

    如果$item变量里面有信息,那么我们将用theme()方法。第一个参数是一个theme钩子。在这个方法中,drupal提供了很多不同theme钩子供我们使用。请参看默认theme钩子列表 (link isexternal)。我们在这里选择了把信息输出成一个无序的列表,那么我们用到了theme_item_list (link isexternal)钩子。第二个参数包含了我们要输出的内容。

    您在这里也许有些糊涂了。为什么drupal会明白theme_item_list呢?我们不是在使用theme()么?其实在这里theme() 在寻找一个钩子,类似于theme_hookname的格式。如果直接使用这个格式的钩子,那么hookname就充当了theme()的第一个参数。在 这里我们用的是_item_list。它定义了我们要输出的默认的格式。

    全部的代码

    <?php
    /**
     * Implements hook_block_view().
     *
     * Prepares the contents of the block.
     */
    function current_posts_block_view($delta = '') {
      switch($delta){
        case 'current_posts':
          $block['subject'] = t('Current posts');
          if(user_access('access content')){
            //Use our custom function toretrieve data.
            $result = current_posts_contents();
            //Array to contain items for theblock to render.
            $items = array();
            //Iterate over the resultset and formatas links.
            foreach ($result as $node){
              $items[] = array(
                'data' =>l($node->title, 'node/' . $node->nid),
              );
            }
            if (empty($items)) { //No content inthe last week.
              $block['content'] = t('Noposts available.'); 
            }
            else {
              //Pass data throughtheme function.
              $block['content'] =theme('item_list', array(
                'items'=> $items));
            }
          }
      }
      return $block;
    }
    ?>

    测试并纠错

    激活模块

          点击模块链接(Modules), 或者访问http://example.com/admin/modules,然后在模块列表中找到other目录。在那里我们应该能够找到'Currentposts'模块。选择激活此模块并保存。现在我们应该能看到在模块名的旁边有一个帮助链接。点击这个链接我们就能看到之前我们在模块里写入的帮助信息 了。

    激活区块

          下一步,我们访问结构->区块, 或者http://example.com/admin/structure/block。下滑到区块列表的底部。在未激活的区块列表中,我们应该能找到区块名为'Current posts'的区块。把它随便设置在Drupal的一个区域中并保存。访问随便一个其他的页面,我们应该能在我们设定的那个区域找到我们的模块了。恭喜,您已经完成了您的第一个模块。

    纠错

          如果当您激活模块时,您遇到了'白屏'或者PHP 错误,很可能您的module文件代码中有语法错误。请检查您代码中的标点符号是否正确,比如冒号,逗号等。然后检查我们的模块名,方法名,前缀和后缀都拼写的正确。(一般我们都可以在apache的日志中找到"白屏"的PHP错误。或者您可以选择改变PHP错误的报告等级 (link isexternal))

    如果您始终无法找到并改正语法错误,那么您将看到“白屏", 因为drupal将持续的试图把我们的模块在每一个页面运行。最简单的修复这个问题的方法是,删除有问题的模块文件夹,或者把文件夹移出drupal的根 目录。这样drupal将不会试图运行这个模块,站点将恢复工作。

    清除缓冲

          如果在您激活了模块之后,您看不到任何变化,很可能是drupal缓冲了很多之前的信息。一般来说,在激活模块时,drupal缓冲不会影响我们。但是在这里我们依然清除缓冲,因为我们尽量把所有纠错方法放到一起,为以后的纠错提供方便和参考。

    访问设置configuration->performance,或者http://example.com/admin/config/development/performance, 我们就可以看见清除缓冲的按钮。

    添加复杂的功能

    生成模块设置页面的准备工作

          现在,我们已经有了一个完全工作的模块。下一步,我们要添加一些复杂的功能在上面。在一个比较复杂的站点,我们也许并不想把所有最近一周所更新的内容链接都显示给用户。所以,我们要建立一个模块的设置页面,来设置到底要显示多少链接给用户。

    注册一个URL

          我们用hook_menu()来定义一个URL, 在这个URL,我们就可以定义我们自己的设置页面了。(原文所说的是用hook_menu()来定义form,这会误导读者。实际上form是在另一个方 法定义的。而hook_menu()只是调用那个方法返回的form,并显示到当前的URL page。 下面是hook_menu()的代码。它将与下一章的代码一起工作。)

    <?php
    /**
    * Implements hook_menu().
    */
    function current_posts_menu() {
      $items = array();
      $items['admin/config/content/current_posts'] = array(
        'title' => 'Current posts',
        'description' => 'Configuration for Current postsmodule',
        'page callback' => 'drupal_get_form',
        'page arguments' => array('current_posts_form'),
        'access arguments' => array('access administrationpages'),
        'type' => MENU_NORMAL_ITEM,
      );
      return $items;
    }
    ?>

     

    创建配置表单

    主要主题描述:表单API

    主要函数说明:variable_get(), system_settings_form()

    接着,我们将编写current_posts_form()函数,给表单添加元素即给$form数组添加元素。它的模式很像menu的$items 变量,它的每个元素名字是它的键,对应的值是特定格式的关联数组。在这个数组中,列出了元素的属性,每个键的名字前面加‘#’。

    添加下面的代码到current_posts.module文件。

    <?php
    /**
     * Page callback: Current posts settings
     *
     * @see current_posts_menu()
     */
    function current_posts_form($form, &$form_state) {
      $form['current_posts_max'] = array(
        '#type' => 'textfield',
        '#title' => t('Maximum number of posts'),
        '#default_value' => variable_get('current_posts_max', 3),
        '#size' => 2,
        '#maxlength' => 2,
        '#description' => t('The maximum number of links todisplay in the block.'),
        '#required' => TRUE,
      );

      return system_settings_form($form);
    }
    ?>

    The element attributes

    Here we create a text field, with a title of 'Maximumnumber of posts', and a description, which will appear below the field. Notethat both of these strings are passed through the t() function for translation.Size is 2, maximum length is 2. The Form API will automatically validate themaxlength attribute when the form is submitted and throw an error if the lengthexceeds 2. We have also designated the field as required. The form API willmark it with an asterisk and will throw an error if the form is submitted withno value in the field. See the forms_api_reference (link isexternal) for the complete list of available elements andproperties.

    Persistent variables

    Certain data, like system settings and user-configurableinformation, needs to be saved and retrievable for a website to functionproperly. Drupal accomplishes this through the use of persistent variables.These variables are stored in a database table with keys provided by theimplementing module.

    The function variable_get() retrieves a persistentvariable, and variable_set(), as you might expect, sets one. We usevariable_get() above to retrieve the value of our field if it has been set. Ifnot, we specify a default value of 3. variable_get() uses the name of theelement, current_posts_max, to find the relevant data.

    System settings

         Drupal makes it easy for us tosave the form's data with the function system_settings_form(). By using thefunction in our code, we tell Drupal to provide a submit button and to savedata into persistent variables using variable_set(). It will also provide agreen confirmation message when data is successfully saved, and a red errormessage if something went wrong. If you prefer, you can create a submitfunction yourself, (see FormAPI Quickstart Guide (link is external))but for now, we will use this handy shortcut.

    Editing the query

    We must add two lines of code to our query function,current_posts_contents, one using variable_get() to retrieve the data from oursettings form, and the other adding the range function to include this limit inthe query. Here's the revised function, with the new lines noted in comments:

    <?php
    function current_posts_contents() {
      //Get today's date.
      $today = getdate();
      //Calculate midnight a week ago.
      $start_time = mktime(0, 0, 0,$today['mon'],($today['mday'] - 7),$today['year']);
      //Get all posts from one week ago to the present.
      $end_time = time();

    //NEW LINE
    $max_num = variable_get('current_posts_max', 3);

      //Use Database API to retrieve current posts.
      $query = db_select('node', 'n')
        ->fields('n', array('nid', 'title', 'created'))
        ->condition('status', 1) //Published.
        ->condition('created', array($start_time, $end_time),'BETWEEN')
        ->orderBy('created', 'DESC') //Most recent first.
        ->range(0, $max_num) //NEW LINE
        ->execute();
      return $query;
    }
    ?>

    Using variable_get(), we save the configuration settinginto the $max_num variable or assign a default of 3. Then we add the rangemethod to our select query. That method's first argument is set to zero tospecify starting at the beginning of the set. The second argument, $max_num,determines how many records to return.

    Add configuration button in Modulespage

    In your current_posts.info file add the following line tocreate a linked button to your configuration page in the modules page.
     

    <?php
    name = Current Posts
    description = A block module that lists links to recent posts.
    core = 7.x

    ; NEW LINE
    configure = admin/config/content/current_posts
    ?>

    Check

    Go ahead and again enable your module. Then you will needto clear the menu cache, so that Drupal will recognize the new URL. (Drupalcaches a lot of data, including a list of all the URLs it recognizes.) To clearthe cache, go to Configuration> Performance orhttp://example.com/admin/config/development/performance, and click the Clear all cachesbutton.

    Now you can test the settings form. Navigate to it: Configuration > Contentauthoring > Current posts orhttp://example.com/admin/config/content/current_posts. Adjust the number oflinks and save the configuration. The maximum number of links in the blockshould adjust accordingly. See how system_settings_form() has added a submitbutton and gives you a confirmation message when you have successfully saved.

    Please note: be sure to disable your module beforecontinuing.

     

    检查数据

           表格的API提供了出色的默认表格数据检查机制,但是也允许我们建立自己的数据检查方法。在这里,我们需要确保用户输入的数字大于0。检查数据的方 法和validate钩子很相似,只是用表格的ID代替了模块名。所以我们的数据核实方法应该叫做current_posts_form_validate($form, &$form_state)。不要把这个与validate钩子想混淆,validate钩子是系统钩子,而我们的方法是自己建立的某一个表格的数据检查方法。

    请注意$form_state队列变量,这个参数在表格方法和数据检查方法中都要用到。$form_state队列中含有表格在每个状态中所包含的 信息和当前状态。用户所输入的数据储存在$_POST变量中,先要被检查是否符合表格的结构,然后会被传送到表格检查方法和提交方法。队列的“value “关键字默认储存了用户所输入的信息。请参看$form_state()的关键字列表drupal_build_form() (link isexternal)

    添加下面的方法到您的current_posts.module模块文件中:

    <?php
    /**
    * Implements validation from the Form API.
    *
    * @param $form
    *   A structured array containing the elements and properties of theform.
    * @param $form_state
    *   An array that stores information about the form's current state
    *   during processing.
    */
    function current_posts_form_validate($form, &$form_state){
      $max_num = $form_state['values']['current_posts_max'];
      if (!ctype_digit($max_num)){
        form_set_error('current_posts_max', t('You must enter anumber for the maximum number of posts to display.'));
      }
    }
    ?>

     

    测试数据

           首先,我们从$form_state队列中获取模块设置表格的数据并把他们存在一个变量中。然后我们便可以查看这是否是一个数字。如果不是,我们就用form_set_error来返回一个错误信息。这个方法的第一个参数表明您的表格的field名,是哪一个field出现了错误。第二个参数是您想 向用户显示的信息内容。这个信息是用红色的框包围起来的。

    如果用户所输入的信息通过了第一个检查,那么我们要检查它是不是一个正数。如果不是,我们还是用form_set_error来显示错误信息。注意错误信息的string是要用t()方法来翻译一下的。

    检查

           激活您的模块并访问设置页面。输入一些数字来看看我们的数据检查方法是不是工作。如果您要添加一些代码,请确保您的模块是未激活的。

    为页面建立自定义的权限

           目前为止,我们已经有了一个区块和一个设置表格。区块显示了所能够显示的最多的链接。但是,也许存在更多的链接,我们的区块的大小不够显示。所以我们需要一个页面来显示更多的最近一周所新建的内容。

    自定义权限

           首先,我们用hook_permission()建立一个定义权限。这个钩子所定义的权限可以在people->Permission页面设置。(http://example.com/admin/people/permissions)只有那些拥有这个权限的用户才能访问我们将要建立的页面

    添加下列代码到我们的模块文件:

    <?php
    /**
    * Implements hook_permission().
    */
    function current_posts_permission(){
      return array(
        'access current_posts content' => array(
          'title' => t('Access content for the Currentposts module'),
        )
      );
    }
    ?>

    这个钩子的定义严格遵循了Drupal队列的模式。重点在于机器可读的钩子名,他应该和我们要建立的hook_menu()名相同。'title'定义了这个权限的名称,它将出现在权限管理页面。这个名称应该被t()方法所翻译。请参看hook_permission() (link isexternal)来查询所有可用的选项参数。

    注册URL并为页面方法命名

           我们将编辑current_posts_menu()钩子,来建立一个路径和新页面的名称。下面是drupal命名的一个快速提示:如果我们要建立的私有的方法,别的模块基本不会用到的,那么我们用下划线来作为方法名的开头,比如_your_module_name_。如果我们的方法是公开的,其他模块会频繁的用到它,我们也不会频繁的更改它的参数,那么我们不用下划线作为名字的开头,比如your_module_name_。如果我们要执行的是一个钩子,那么我们必须遵循your_module_name_hookname的格式。所以,如果我们没有执行一个钩子,我们已定要检查我们的命名没有恰巧符合一个钩子的格式,否则系统将认为这是一个钩子。

    页面的callback是一个非常好的私有方法的例子。所以我们的命名起始与一个下划线+模块名+'page'。把下面的代码作为队列的第二个元素 添加到我们的current_posts_menu()方法。确保我们 return $items是方法的最后一行,记住不要包含php的开始和结束符。

    <?php
      $items['current_posts'] = array(
        'title' => 'Current posts',
        'page callback' => '_current_posts_page',
        'access arguments' => array('access current_postscontent'),
        'type' => MENU_NORMAL_ITEM, //Will appear in Navigationmenu.
      );
    ?>

    把这个menu item和前一个menu item做对比。前一个item需要有description作为设置页面的描述。在这里,这是不重要的。因为我们将建立的页面方法不会调用 drupal_get_form,我们并没有任何页面参数要传进去。新建的权限将成为我们的accessargument。因为我们没有用admin/config路径,所以我们的页面链接将出现在Navigation menu。

    检查

           激活我们的模块,并检查Navigation menu。我们应该能看到Current posts的链接。如果看不到,请试着清空缓冲,然后再试试。因为我们还没有编写页面方法,所以这个链接将引导我们到一个空白页面,或者错误信息。在继续编写我们的模块前,请记住卸载当前模块。

    启用数据库命令

           我们要新建的页面方法基本会与 current_posts_block_view的功能相同,它将从我们要建立的current_posts_contents方法中获得数据。所以我们需要在current_posts_contents方法中来读取数据库并获得数据。我们所需要做的就是编辑这个方法,调用一些数据库命令,然后把返回的信息传给页面。

    下面是我们要编辑的代码:

    <?php
    function current_posts_contents($display){   //$display argument isnew.
      //Get today's date.
      $today = getdate();
      //Calculate midnight a week ago.
      $start_time = mktime(0, 0, 0,$today['mon'],($today['mday'] - 7),$today['year']);
      //Get all posts from one week ago to the present.
      $end_time = time();
     
      $max_num = variable_get('current_posts_max', 3);
     
      //Use Database API to retrieve current posts.
      $query = db_select('node', 'n')
        ->fields('n', array('nid', 'title', 'created'))
        ->condition('status', 1) //Published.
        ->condition('created', array($start_time, $end_time),'BETWEEN')
        ->orderBy('created', 'DESC'); //Most recent first. Querypaused here.

       if ($display == 'block'){
      // Restrict the range if called with 'block' argument.
        $query->range(0, $max_num);
      } //Now proceeds to execute().
      //If called by page, query proceeds directly to execute().
     
      return $query->execute();
    }
    ?>

    首先,我们把$display这个变量假如到方法中。然后在数据库命令构建结束后,我们来判断这个变量,来决定如何继续构建和运行数据库命令。如果这个变量是'block', 表明是block在请求信息,那么我们则限制选择的范围,因为block能显示的条目有限。如果变量参数是'page'。表明是page页面在请求信息, 那么我们将不对数据库选择范围进行限制,页面将显示所有信息。

    编辑current_posts_block_view

    如果我们要想我们的代码像current_posts_block_view一样工作,必须要在block区块的显示代码中加入类似下面的代码来调用我们所建立的方法。

    <?php
    $result = current_posts_contents('block');
    ?>

    要想代码工作得像block_view一样,只需把‘block’作为参数传到方法中。

     

     

    主题化页面

    现在我们将要编写一个页面方法,来把页面内容返回到我们之前建立的current_posts_menu()钩子中去。下面是代码的第一部分:

    <?php
    /**
    * Custom page callback function, declared in current_posts_menu().
    */
    function _current_posts_page() {
      $result = current_posts_contents('page');
      //Array to contain items for the page to render.
      $items = array();
      //Iterate over the resultset and format as links.
      foreach ($result as $node) {
        $items[] = array(
        'data' => l($node->title, 'node/' . $node->nid),
        );
      }
    ?>

    是不是看上去跟之前所编写的代码很相似?我们调用之前建立的方法来读取数据库信息,传入参数'page‘, 这样程序就会返回所有的信息。然后我们就可以遍历返回的信息,为美一个节点生成链接。

    解析队列

           下面我们将进入Drupal的主题系统来把我们的队列内容输出成特定的格式。drupal7 (link is external)总是把页面的内容储存在队列里,然后主题系统将才把内容打印输出。这样我们就尽可能长时间的可以把内容当作数据来处理,而不是HTML代码。请参看更完整的解释 Render Arrays in Drupal 7 (link is external)下面的代码包含了我们第一个可解析的队列:

    <?php
      if (empty($items)) { //No content in the last week.
        $page_array['current_posts_arguments'] = array(
          //Title serves as page subtitle
          '#title' => t('All posts from the lastweek'),
          '#markup' => t('No posts available.'),
        );
        return $page_array;
      }
    ?>

    这段代码跟current_posts_block_view()方法(当没有信息显示时)的显示效果一样。我们建立一个队列变量$page_array。变量名只要不合其他变量方法名冲突,是无关紧要的。为了编码的一致性,我们沿用模块名为变量名的前缀。

    子队列严格的遵循可解析队列的格式。元素名前缀为"#",应用解析队列API中的元素。#title元素定义了这个队列包含的内容的标题。页面的标 题是由curent_posts_menu()定义的,所以这里定义的是子标题。#markup是最简单的元素,在这里提供一个您要显示的string。 您也可以用HTML代码。只要用t()方法来翻译他们即可。

    您可能发现这个方法看上去与我们之前建立的设置表格的方法相似。他们都用‘#’来当作元素的前缀。form API总是用render API的元素,但是我们之前没有很好的把这一部分写到文档里。现在在drupal7,我们能够从form API索引 (link is external)找到所有form API所用到的元素。

    主题钩子的建议

    下面是代码的最后一部分:

    <?php
        
      else {
        $page_array['current_posts_arguments'] = array(
          '#title' => t('All posts from the lastweek'),
          '#items' => $items,
          //Theme hook with suggestion.
          '#theme' => 'item_list__current_posts',
        );
        return $page_array;
      }
    }
    ?>

    我们又用到了#title元素。跟着的两行我们调用theme_item_list()来解析队列。在 current_posts_block_view()方法中,我们用到了下面的代码:theme('item_list',array('items' => $items));,它们的功能是一样的。$items变量包含了一个链接的队列,被付给了#items元素,#theme元素定义了我们将用 drupal默认的item lists作为主题输出格式。

    在这里我们注意到一个细节。我们在主题钩子调用是用了两个下划线加模块名。这样是为了给其他主题编写者更大的自由。如果我们在这里只用默认的主题钩子,其他主题编写者做的任何改动将被应用于这个钩子,不只是我们的列表。在这里,主题将只修改我们的本模块的输出格式。

    两个下划线是drupal认识的格式,它表示这是一个建议的主题钩子。如果您需要,您可以编写任意的有双下划线的建议。drupal将优先查找建议的主题钩子,然后才是默认的钩子。本模块并不使用建议的主题钩子,但是这是可以被加入的。

    检查

    是时候检查我们的模块了。 激活我们的模块并点击Navigation目录的链接。如果我们是未登录状态或者没有权限访问,我们将无法看到链接。

    Adding a 'More' link

    Maintopic described: Block system, Render arrays, Menu system
    Main functiondescribed: drupal_set_title()

    With this last addition to the module, we will pulltogether what you have learned about the block system, the menu system, andrender arrays, and throw in a workaround for a minor bug in the current versionof Drupal 7.

    It may have occurred to you that access to this pagebelongs not in the 'Navigation' menu, but through a 'More' link at the bottomof the 'Current posts' block. That was the plan all along, but we started witha menu link to show how it works.

    If you looked at the Default theme implementations(link is external) referenced back in Generating block content (link is external), you may have noticedthe theme_more_link (link isexternal) theme hook. We'll use that, along with a theme hooksuggestion, to theme the 'More' link for our block.

    Child render elements

    We'll start by converting the call to theme_item_list intoa render array as we did in the page function. We'll also make it a child ofblock['content'] to allow for the 'More' link as a sibling. Here's the revisedcode for the last section of current_posts_block_view:

    <?php
    else {
      //Pass data through theme function.
      $block['content']['posts'] = array(
        '#theme' => 'item_list__current_posts__block',
        '#items' => $items,
      );
    ?>

    We move the item list code to $block['content']['posts'],making it a child element. You can name the child element whatever you like, aslong as it doesn't start with a hash mark (#). We convert the call totheme_item_list into a render array, then add the __current_posts theme hooksuggestion. This we follow with a second suggestion in case a themer wants torender the lists in the block and page differently.

    Here's new code for the 'More' link to add directly afterthe last listing:

    <?php
      //Add a link to the page for more entries.
      $block['content']['more'] = array(
        '#theme' => 'more_link__current_posts',
        '#url' => 'current_posts',
        '#title' => t('See the full list of current posts.'),
      );
    }
    ?>

    Here we make the 'More' link a sibling to the posts arrayand provide the two parameters this theme hook requires, the path and title.The title provides text which will appear as a tooltip when the mouse hoversover the link. We give this theme hook a suggestion as well.

    Edit current_posts_menu()

    At this point, we have links to the page both in our blockand in the 'Navigation' menu. We don't want a link in the menu, so we'll editcurrent_posts_menu() to take it off. All you need to do is change the typeattribute for the page item. Delete MENU_NORMAL_ITEM and replace it withMENU_CALLBACK. This type provides a path and attributes only, with no menulink. Should you have your module enabled, you'll need to disable thenre-enable it for this change to take effect.

    Page title fix

    Enable your module and follow the 'More' link in the block,then note the page title. 'Home'? Not what we had in mind. This is caused by abug in the first version of Drupal 7. (If you see the 'Current posts' title,you are using a later version that is not in release at the time of thiswriting.) There is a patch in the works, but we need to code for the currentrelease. Even when the bug is fixed, we can't be certain that our module won'tbe used on an installation that has not been updated.

    The workaround is to use the function drupal_set_title().Add the following to the beginning of your _current_posts_page() function:

    <?php
      drupal_set_title('Current posts');
    ?>

    With this addition, you can be sure the correct title willappear on the page, no matter which version of Drupal 7 your module isinstalled in.

    Check

    Check your module functionality one final time. If you havebeen following along, the last thing to check is the corrected page title,'Current posts'. If you have problems getting your code additions to appear,try clearing the caches or disabling then re-enabling your module.

    View the code

    You can view all the code for the .module file here: current_posts.module (link isexternal)

    用SimpleTest测试

    Drupal 7的内核包含了测试的功能。Drupal 的SimpleTest模块是基于SimpleTestPHP Library (link is external)的。如果您已经很熟系这个框架了,那么您将能更轻松的理解如何把这个框架应用到Drupal中。

    Drupal测试相比于元素测试,更偏向于功能测试。功能测试将不会检查个体方法或代码,它偏向于检查测试整体的交互界面功能。这个方法更是用于Drupal。我们既要开始编写一个测试了,我们的测试程序将检查我们模块的四个钩子是不是能够如期的运行。

    编写一个好的测试代码的一个挑战是您是否清楚哪些是您需要测试的,您从测试代码中所获得信息并不会告诉您我们的模块到底是如何工作的。这些测试将把您引向一个测试环境其中包含一些假定。想讨论如何设计您的测试程序,请访问SimpleTest教程的Figuringout what we need to test (link is external)部分。当您熟悉了这个基本部分之后,您将能够分析和理解drupal.org网站的SimpleTest部分其他的教程和示例。

    建立测试文件

    首先,我们将建立并注册一个测试文档。测试文档的命名必须遵从{modulename}.test的格式。我们在current_posts的目录下建立一个名叫current_posts.test的文件。注意我们的文件只包括php的起始符,并没有结束符。

    和数据库API一样,SimpleTest的框架也使用面向对象代码。测试被编写在类里面。就像在TellingDrupal about your module (link is external)里面讲到的,所有模块必须在.info文件中把自己的类声明。我们把下面的代码添加到current_posts.info文件的结尾:

    files[] = current_posts.test

    类的继承

    接着,我们将添加一个区块,并开始编写测数代码。将下面的代码添加到我们的测试文件中:

    <?php
    /**
    * @file
    * Tests for the Current posts module
    */

    class CurrentPostsTestCase extends DrupalWebTestCase{
      public static function getInfo(){
        return array(
          'name' => 'Current posts modulefunctionality',
          'description' => 'Tests hooks in the Currentposts module',
          'group' => 'Current posts',
        );
      }
    ?>

    我们将把我们的测试编写成CurrentPostsTestCase类的方法。注意大写的标准,类名中的每一个单词首字母必须大写。在类的方法名中,第一个单词小写,后面的单词首字母大写。比如getInfo()。下划线和减号是不能用在类名和方法名中的。

    基本类DrupalWebTestCase为我们提供了一个测试的基本框架。它建立了一个内部的drupal站点和浏览器之间的联系。请参看API functions (link is external)来了解它的全部功能。SimpleTest还提供了一个类DrupalUnitTestCase,作为元素测试,这并不需要drupal bootstrap的支持,换句话说,我们不需要drupal来运行元素测试。

    getInfo()方法对drupal的SimpleTest来说是必须的。我们必须有这个方法才能运行。队列里面的元素定义的我们的测试在drupal中的显示。

    setUp()方法

    下面的方法看上去很简单:

    <?php
      public function setUp(){
        parent::setUp('current_posts');
      }
    ?>

    这个方法允许我们调用父类中的一个方法来进行必要的设置,我们可以建立一个新用户,给它一些权限,把用户登录。我们在这里要做的就是把我们要安装的 模块作为一个参数来传进去。在这里,我们传的是“current_posts“。没有这个方法,我们也可以运行我们的测试,但是一般来讲,我们都需要这个 方法来进行一些设置。

    第一个测试和module_invoke()

    下一步,添加下面的代码:

    <?php
      public function testPermission(){
        $data = module_invoke('current_posts', 'permission');
    ?>

    我们将开始测试我们最简单的一个钩子current_posts_permission()。我们建立一个变量来记录 module_invoke()返回的值。这个方法需要两个参数:模块名和钩子名。当测试运行时,它将等同于运行 current_posts_permission()。每一个测试都开始与这个方法的调用,

    假定

    当我们激活了一个钩子,假定assertions (link is external)将执行实际的测试。一组方法将检查是否符合条件并返回一个布尔值。如果是TRUE,测试将通过。反之,测试将失败。它将遵循$this->assertion(condition(s), message)的格式,message是需要被t()方法翻译的,它将在测试运行时显示那一部分被测试了/

    把下面的钩子测试方法添加到testPermission()方法:

    <?php
        $this->assertTrue(is_array($data), t('Permission hookreturns array.'));
      
        $this->assertTrue(array_key_exists('access current_postscontent', $data),
        t('Permission is as expected.'));
      }
    ?>

    assertTrue 将检查第一个参数是否为TRUE,第二个参数是测试运行结束后将要显示的信息。我们要检查第一个方法是不是返回一个队列,如果我们权限的名字是队列中的 key,那么信息将显示我需要的结果。这就是我们测试current_posts_permission()的方法。

    测试equality

    下面我们来测试menu钩子。添加下面的代码到测试文档:

    <?php
      public function testMenu(){
        $items = module_invoke('current_posts', 'menu');
      
        $this->assertEqual(2, count($items), t('Two items inmenu.'));
      
       $this->assertIdentical($items['admin/config/content/current_posts']['title'],
        $items['current_posts']['title'], t('Titles areidentical.'));
      }
    ?>

    在激活了模块之后,我们用到另外两个假定。assertEqual()就跟==的作用一样。它假设两个条件是相等的。在这里代表所建立的menu等于2。count($items)会返回menu的个数。

    assertIdentical 就像===一样。我们可以用它来检查两个menu的title是否一模一样。

    另一个假定

    下面是另一个方法的代码:

    <?php
      public function testBlockView(){
        $data = module_invoke('current_posts', 'block_view','current_posts');
           
        $this->assertEqual(t('Current posts'), $data['subject'],t('Subject is as expected.'));
      
        $this->assertNotNull($data['content'], t('Block containsdata.'));
      }
    ?>

    在我们要测试current_posts_block_view()的方法中,我们在module_invoke()中有了第三个参数。这个参数告 诉方法在特定时候要调用current_posts方法。我们首先用equality来检查区块的title。我们要小心类似的测试。如果我们改动了区块的title而没有改相应的测试代码,那么这个测试将break。这个方法使用与测试那些不容易改变的变量。

    下一步我们用assertNotNull()来检查第一个参数是不是空。我们希望变量中包含信息。

    2个负面的假设

    添加下面的代码来测试current_posts_block_info():

    <?php
      public function testBlockInfo(){
        $info = module_invoke('current_posts', 'block_info');
      
        $this->assertNotEqual('DRUPAL_CACHE_PER_USER',$info['current_posts']['cache'],
        t('Cache is not set to DRUPAL_CACHE_PER_USER'));
      
        $this->assertFalse(count($info) > 1, t('No more thanone block defined.'));
      }
    ?>

    assertNotEqual()相当与假设不等于。将第一个已知的变量和第二个未知的测试变量进行比较。这里我们要检查缓冲是不是没有被设置成DRUPAL_CACHE_PER_USER。注意,在这里我们用了一个附加的队列元素来解析测试变量。

    在assertFalse中,我们用一个公式来检查这个方法是不是只定义了一个区块。

    激活两个钩子

    把下面的代码方法加到我们的测试文件中来完成测试的最后一部分:

    <?php
      public function testBlock(){
        $info = module_invoke('current_posts', 'block_info');
        $data = module_invoke('current_posts', 'block_view','current_posts');
        
        $this->assertIdentical($info['current_posts']['info'],$data['subject'], t('Block list
        name and subject are identical.'));
      }
    }
    ?>

    在这里我们激活两个区块方法来比较每个方法中的变量。我们用到了===假定,这是我们测试的最后一部分。

    检查

    SimpleTest是核心模块的一部分,但是并不在默认安装的模块内。请访问模块页面(http://example.com/admin/modules (link is external)), 在核心模块的部分查找一个叫做’Testing‘的模块并激活它。在设置页面确保‘Provide verboseinformation when running tests‘别选择。我们从测试中得到越多的信息越好。

    现在激活我们的Current posts模块。如果模块已经激活,那么卸载并重新激活一遍。访问‘Testing’页面(http://example.com/admin/config/development/testing (link is external)),并在测试列表中查找'Current posts' 。如果您已经运行了测试,您需要在页面的中下方点击'Cleanenvironment'来让我们的测试显示正常。

    当一切准备就绪,选择'Current posts'测试的选项并点击'Runtests'按钮。我们将看见一个进度条来显示测试的进度。请耐心,测试模块将用较长时间来建立一个虚拟环境并运行所有的测试。注意,进度条下面的文字将和我们在代码中getInfo()方法的name元素吻合。

    当测试结束,我们将看见一个提示信息来告诉我们测试通过,失败,或者有错误抛出。想查看具体信息,请点击测试名下方的链接'Currentposts module functionality‘。绿色背景的’pass'表示测试的结果和我们希望的结果相同。红色背景的‘fail'表示结果不想同。黄色背景 的’exception'表示测试外有异常抛出,比如PHP警告。如果遇到fail或者exception,请检查您的代码,并重新测试知道通过为止。

    查看代码

    您可以查看这个测试的代码文件:current_posts.test (link isexternal)

    Patch练习

    恭喜,现在您已经能够编写简单的模块并测试了。但是在现实世界中,我们基本不会有机会从零开始编写自己的模块,多数情况下,Drupal已经有一个模块能满足或者接近满足我们的需要。所以如果您要为一个模块提供新的功能或者纠正错误,您共享到整个社区。

    在这部分,您将有机会来练习为一个模块建立patch和分析别人为您的模块编写的patch。比如说,我们的模块发布了,但是没有‘more'链 接,我们可以提供这个功能。我们将用一个特殊的模块'Current content'代替'Current posts'。您可以把这个模块建立到http://example.com/sites/all/modules路径,来避免与我们之前写的 'Current posts'模块产生冲突。

    我们的任务是用drupal默认的版本控制系统GIT, 来复制工作区的根目录,建立子分支,在添加了‘more'功能后,提交一个patch。如果您还能想出更多的功能,您也可以一起添加到新功能里。然后把您的patch提交到'Current content' 的报错队列中。下一步,审查在队列中由别人编写的在其他子分支的patch。如果这个patch工作无误,那么我们将把他的状态改为‘审核并测试通过’ (RTBC)。(大多数的patch很难达到这个状态,所以在提交patch前,请仔细审查您的patch)如果有人在提交patch的同时还审核 patch,那么您的patch也将会被审核。

    连接到GIT

    Drupal开发社区用GIT来管理drupal所有版本的所有文件。如果您还不熟悉GIT,请参阅一些出色的文档,从Introductionto Git (link is external)开始。您将需要连接到GIT到您的电脑,请参看Installing Git (link is external)

    要应用drupal.org的GIT,您必须在drupal.org有一个账户,并在您的资料编辑页面接受'Git accessagreement'。请参看Obtaining Git access (link isexternal)来获得更详细的说明,然后到Identifying yourself toGit (link is external)来完成您的设置。

    复制工作区根目录

    现在我们可以开始建立patch了。首先,访问Currentcontent (link is external)的 开发沙盘。在页面的顶部,查看'Version control'链接,然后在您的电脑上启动终端,然后输入命令来复制工作区。(本部分的说明是基于命令行的,如果您用的是图形界面,您需要把命令转化。)如果您吧代码复制到了http://example.com/sites/all/modules目录,您可以检查功能并运行测试,

    建立patch

    在GIT中,不同的路径可能会链接到同一个终点。这就是建立patch的真谛。如果您已经有了您自己的流程,只是想练习一下,请继续您自己的流程。在这里,我们将用子分支来联系,这样我们就可以建立并审查patch,而不会影响到模块的源代码了。如果您对以上的说明有意问,请参看下面的其他选择。

    'Currentcontent' (link is external)队列中,在‘新功能需求’目录下,建立一个新issue。请注意issue号,这将是您issue的url,用下面的命令建立一个子分支:

    git checkout -b more_patch-[issue-number]

    checkout告诉GIT你在改变一个子分支。-b的意思是您在建立的是一个新的子分支,后面跟着是名字。您可以随便为子分支命名,但是在这里我们建议用issue号,这样就不会于其他issue混淆。我们在这里把batch加到名字里来与我们将审核的batch区分。

    现在您可以修改.module文件了,您可以选用任意的编辑器。只要您是在子分支上工作,您的修改就不会影响到主分支的代码。要添加‘more'链 接,您需要修改menu方法,page方法,和block view方法中的theme_item_list,就像我们在之前的章节 (link is external)中做的那样。请在您的开发站点中查询相关的代码,让你后您可以运行'Current content'的测试,应该都可以通过。

    当您对您的修改满意时,您就可以把修改上传到GIT。在命令行(或者图形界面)中,运行下面的命令:

    git status

    status命令会告诉您您目前处于patch的子分支上,它还会告诉您您的.module文件已经被修改,但是还没有上传。下一个命令:

    git diff

    这个命令对您的修改做了最后的检查,来确保您已经包含了所有需要上传的。如果您不小心包含了空格在里面,GIT将把它标出来。确保您把空格都删除,确保所有格式和修改都是正确的,我们的目标是用一次上传就把所有的修改完成。

    当您准备好上传您的修改。-A参数将把所有的修改上传到子分支。包括新文件,修改了的文件,和删除了的文件:

    git add -A

    如果您在这时候运行git status, GIT将会把您的文件放到'Changes to becommitted'目录下,现在实施修改:

    git commit -m "Issue #[issue number] by [your drupal.org username]:Added a 'More' link"

    commit信息严格的遵循drupal的格式,请参看Commitmessages - providing history and credit (link is external)来查询详细的解释。下一步为了防止以外的更改,我们获取最新的代码:

    git fetch origin

    GIT将提示您正在从drupal.org的'Current content'工作区获取并解压对象。下一步在刚获取的文件中做我们刚才做过的更改:

    git rebase origin/master

    您将收到信息,告诉您GIT已经从头修改分支上的文件,现在您已经准备好建立您的patch文件了。

    建立patch文件

    我们有两种方法来建立patch文件:

    • 1.只上传更改一次。如果您在一次commit中完成了所有更改,那么您可以用git format-patch命令来建立patch文件并包含您的信息: git format-patch origin/master --stdout > added_more_link-[issue number]-[comment number].patch

    确保您没有井号在文件名里,不然文件将不会被接受。

    • 2.多次长传。如果您上传了多次,那么您可以用git diff: git diff origin/master > added_more_link-[issue number]-[comment number].patch

    当然,如果您愿意,您夜可以用别的名字。如果您是为core模块写的patch,那这里的将会用core模块的版本号好作为您patch的版本号。如果是其他模块,版本号可能会不同。(如果您在作出注释之前就已经建立了patch,那么您可以推算出注释号。)

    当您运行了这个命令,GIT将提交这个patch,但是不会显示任何信息。您将在您当前的工作区的根目录下找到patch文件。(这个工作区只有一 个文件夹,如果有多层子文件夹,那么这个patch文件将会被保存在最上层的文件夹。)打开这个文件,并检查您的修改。如果您用的是git format-patch命令,这个patch文件将起始于井号,作者的名字,邮件地址,建立时间和上传信息。如果您用的是git diff命令,这个patch文件将用diff作为起始。如果您只是为了‘more’链接做了修改,那么您的patch文件应该包括三部分。每部分将用行 号和修改的方法作为起始。

    现在返回到'Current content'的问题队列,上传您的patch,把状态改成‘Needs review‘。

    如果您现在运行git status命令,您将发现您的patch文件已经被包括了。为了您不会重复上传这个patch文件,请用下面的命令移除文件:

    rm added_more_link-[issue number]-[comment number].patch

    审核patch

    现在我们已经建立了一个patch,是时候审核别人的patch了。首先选择一个patch,并点击它名字上的链接来浏览。把issue号记录下来。

    然后回到命令行,进入current_content目录,检查状态:

    git status

    如果您不在主工作区,请用下面的命令:

    git checkout master

    下面要确保您在用最新的版本的代码:

    git pull

    为审核这个patch建立一个新的子分支:

    git checkout -b more_review-[issue number]

    有集中不同的方法来下载和应用这个patch。如果您要用curl,请查看这个链接 (link is external)在 'Reviewing patches'下面的内容。我们在这里将用Unix命令wget:

    wget http://drupal.org/files/issues/[patchname].patch
    git apply -v [patchname].patch

    当您下载这个patch时,您将获得很多数据,当您用-v参数时,您将得到一个信息来告诉您是否已经把这个patch应用成功。

    如果您现在运行git status,您将看到这个patch已经被包含了。为了您在将来不会重复上传这个patch,请用下面的Unix rm命令来移除此文件:

    rm [patchname].patch

    检查代码的变化:

    git diff

    您也可以用其他编辑器查看文件。

    现在检查patch。因为现在的sandboxes已经不包括testbot的功能,所以您需要在您的站点上用SimpleTest来测试。请留意您所测试的,哪些工作,哪些不工作。并在issue中写明。要查看如何审核patch的细节,请访问Reviewingpatches (link is external)

    如果在您测试中发现什么问题,请把issue的状态改为'Needs work'。如果工作无误,请把状态改成RTBC。注意,在真是的issue队列中,一般需要2人以上来认证patch和把状态改为RTBC。请参看链接 (link is external)来了解如何适当的改变issue状态。

    总结

    我们终于到了这个教程的结尾。如果您是从头做下来的话,您应该已经熟悉了钩子系统,区块系统,目录系统,数据库系统,表格系统,解析队列,页面方法,一些drupal方法,SimpleTest,和用GIT提交patch。

    下一步做什么呢?看一下 Workingwith the Drupal AP (link is external)Examples forDevelopers (link is external), 那些是您所感兴趣的。

     

     

     


    展开全文
  • Drupal 常用模块汇总

    千次阅读 2019-03-28 16:00:56
    1.为Drupal增加视频播放功能 使用video和flowplayerhttp://drupal.org/project/videohttp://drupal.org/project/flowplayer2.为Drupal增加邮件服务器 SMTP和PHPMailer 使用SMTP和phpmailerhttp://drupal.org/project...
  • Drupal 7宝典

    热门讨论 2012-09-10 14:49:27
    完成《Drupal 7宝典》的学习后,您就可以去构建和维护一个基于Drupal的Web站点了。 Drupal是当前风头正劲的开源内容管理系统(content management system,CMS)崛起大潮中的佼佼者。在过去,使用一个Web内容管理系统...
  • drupal 入门

    千次阅读 2016-09-12 16:34:11
    2016年9月11日drupal深圳社区组织了一次聚会,做了个D8入门分享,这里整理了一下,D8正式版是2015年11月19日发布,我也是从这个时候开始学习研究drupal ,过去差不多一年时间了,入门过程的很多东西记忆犹新,可能...
  • drupal 简介

    2013-12-04 14:43:03
    Drupal是用作建设网站的。它是一个高度模块化,开源的web内容管理框架,它重点建 立在合作之上的。它是一个可扩展的,适应标准的,并努力保持简洁代码和较小脚本的系统。 Drupal 发布版中包含基本的核心功能,其他的...
  • Drupal时区信息

    2010-04-24 19:08:00
    <p>In my drupal installation I can get the offset from UTC in seconds to the timezone I specified in the admin panel using <pre><code>variable_get('date_default_timezone', 0) </code></pre> <p>...
  • Drupal

    2018-12-12 23:54:44
    Drupal &lt; 7.32 “Drupalgeddon” SQL注入漏洞(CVE-2014-3704) Drupal 是一款用量庞大的CMS,其7.0~7.31版本中存在一处无需认证的SQL漏洞。通过该漏洞,攻击者可以执行任意SQL语句,插入、修改管理员信息,...
  • Drupal

    2011-03-22 09:23:07
    Drupal没有像WordPress那样普及,或许是因为它稍有难度来学习。但是如果你创建大的网站,使用Drupal,高度灵活的开源CMS,可以得到你想要的更漂亮、更强大的各种类型的网站。 下面是8种类型的网站,使用Drupal来...
  • drupal 常用模块

    万次阅读 2014-04-04 19:00:37
    1、drupal快速上手的commerce distro商城模块 http://drupal.org/project/commerce_kickstart 虽然commerce kickstart是一个好的入门,但仍然要花时间来设定才能用地上。因此今年内会大大更新到2.0,功能大跃进,...
  • Drupal

    2015-02-16 18:39:01
    Drupal
  • drupal模块列表 drupal常用模块

    千次阅读 2013-08-29 18:26:11
    1、drupal快速上手的commerce distro商城模块 http://drupal.org/project/commerce_kickstart 虽然commerce kickstart是一个好的入门,但仍然要花时间来设定才能用地上。因此今年内会大大更新到2.0,功能大跃进,...
  • drupal

    2017-01-18 10:13:04
    lnmp 搭建drupal 有时间写下
  • Drupal

    2007-11-12 09:10:00
    Drupal 是一个开源的内容管理系统(CMS)平台,用于构造提供多种功能和服务的动态网站,这些功能包括用户管理(User Administration)、发布工作流(Publishing Workflow)、讨论、新闻聚合(News Aggregation)、元数据...
  • 主题说明了Drupal网站的用户界面(UI)。 即使主题结构没有明显改变,Drupal版本7还是带有一种新的主​​题实现方法。 本文介绍了如何创建新的Drupal 7主题。 Drupal主题的目的是将框架的处理逻辑与设计元素分开。...
  • Symfony没有数据库组件,drupal8完全自己实现了一个基于php的pdo扩展的数据库系统,它提供了一个数据库抽象层,让你可以使用统一的方式去操作数据库,而不用管底层使用的是什么数据库,只需要使用好它提供的接口...
  • drupal

    2014-05-14 19:51:00
    一 devel 模块 配置-》development-&gt;generate users 添加测试账号   php区块 结构-》区块-》switch user... 使用者切换 结构-》区块-》switch user ...网址 = drupal 命令 = 菜单 多数情况下,网址代表的,...

空空如也

1 2 3 4 5 ... 20
收藏数 15,676
精华内容 6,270
关键字:

drupal