Pro Drupal 7 Development
Third Edition
■ ■ ■
ProDrupal7Development:ThirdEdition e-mailorders-ny@springer-sbm.com,orvisitwww.springeronline.com.
Forinformationontranslations,pleasee-mailrights@apress.com,orvisitwww.apress.com.
ApressandfriendsofEDbooksmaybepurchasedinbulkforacademic,corporate,orpromotionaluse. eBookversionsandlicensesarealsoavailableformosttitles.Formoreinformation,referenceour SpecialBulkSales–eBookLicensingwebpageatwww.apress.com/info/bulksales.
Theinformationinthisbookisdistributedonan“asis”basis,withoutwarranty.Althoughevery precautionhasbeentakeninthepreparationofthiswork,neithertheauthor(s)norApressshallhave anyliabilitytoanypersonorentitywithrespecttoanylossordamagecausedorallegedtobecaused directlyorindirectlybytheinformationcontainedinthiswork.
Contents at a Glance
Foreword...xxv
AbouttheAuthors...xxvi
AbouttheTechnicalReviewers...xxvii
Acknowledgments...xxviii
Introduction...xxix
■
Chapter1:HowDrupalWorks...1
■
Chapter2:WritingaModule...13
■
Chapter3:Hooks,Actions,andTriggers...33
■
Chapter4:TheMenuSystem...57
■
Chapter5:WorkingwithDatabases...89
■
Chapter6:WorkingwithUsers...115
■
Chapter7:WorkingwithNodes...137
■
Chapter8:WorkingwithFields...163
■
Chapter9:TheThemeSystem...185
■
Chapter10:WorkingwithBlocks...223
■
Chapter11:TheFormAPI...239
■
Chapter12:ManipulatingUserInput:TheFilterSystem...295
■
Chapter13:SearchingandIndexingContent...307
■ CONTENTSATAGLANCE
■
Chapter15:WorkingwithTaxonomy...343
■
Chapter16:Caching...365
■
Chapter17:Sessions...379
■
Chapter18:UsingjQuery...389
■
Chapter10:LocalizationandTranslation...417
■
Chapter20:XML-RPC...451
■
Chapter21:WritingSecureCode...465
■
Chapter22:DevelopmentBestPractices...487
■
Chapter23:OptimizingDrupal...499
■
Chapter24:InstallationProfiles...525
■
Chapter25:Testing...545
■
AppendixA:DatabaseTableReference...565
■
AppendixB:Resources...623
Contents
Foreword...xxv
AbouttheAuthors...xxvi
AbouttheTechnicalReviewers...xxvii
Acknowledgments...xxviii
Introduction...xxix
■
Chapter1:HowDrupalWorks...1
WhatIsDrupal?...1
TechnologyStack...1
Core...2
AdministrativeInterface...3
Modules...3
Hooks...5
Themes...5
Nodes...6
Fields...6
Blocks...6
FileLayout...6
ServingaRequest...9
TheWebServer’sRole...9
■ CONTENTS
ProcessingaRequest...10
ThemingtheData...11
Summary...11
■
Chapter2:WritingaModule...13
CreatingtheFiles...13
ImplementingaHook...15
AddingModule-SpecificSettings...17
DefiningYourOwnAdministrationSection...25
PresentingaSettingsFormtotheUser...26
ValidatingUser-SubmittedSettings...29
StoringSettings...29
UsingDrupal’svariablesTable...29
RetrievingStoredValueswithvariable_get()...30
FurtherSteps...30
Summary...31
■
Chapter3:Hooks,Actions,andTriggers...33
UnderstandingEventsandTriggers...33
UnderstandingActions...35
TheTriggerUserInterface...35
YourFirstAction...38
AssigningtheAction...39
ChangingWhichTriggersanActionSupports...40
UsingtheContextinActions...45
HowtheTriggerModulePreparestheContext...45
ChangingExistingActionswithaction_info_alter()...46
HowActionsAreStored...49
TheactionsTable...49
ActionIDs...49
CallinganActionDirectlywithactions_do()...50
DefiningYourOwnTriggerswithhook_trigger_info()...51
AddingTriggerstoExistingHooks...54
Summary...55
■
Chapter4:TheMenuSystem...57
CallbackMapping...57
MappingURLstoFunctions...57
CreatingaMenuItem...61
PageCallbackArguments...64
PageCallbacksinOtherFiles...67
AddingaLinktotheNavigationBlock...68
MenuNesting...69
AccessControl...70
TitleLocalizationandCustomization...72
DefiningaTitleCallback...72
WildcardsinMenuItems...74
BasicWildcards...74
WildcardsandPageCallbackParameters...75
UsingtheValueofaWildcard...75
WildcardsandParameterReplacement...77
PassingAdditionalArgumentstotheLoadFunction...78
Special,PredefinedLoadArguments:%mapand%index...79
BuildingPathsfromWildcardsUsingto_arg()Functions...79
■ CONTENTS
AlteringMenuItemsfromOtherModules...80
AlteringMenuLinksfromOtherModules...82
KindsofMenuItems...82
CommonTasks...84
AssigningCallbacksWithoutAddingaLinktotheMenu...85
DisplayingMenuItemsAsTabs...85
HidingExistingMenuItems...87
Usingmenu.module...87
CommonMistakes...88
Summary...88
■
Chapter5:WorkingwithDatabases...89
DefiningDatabaseParameters...89
UnderstandingtheDatabaseAbstractionLayer...90
ConnectingtotheDatabase...91
PerformingSimpleQueries...92
RetrievingQueryResults...93
GettingaSingleValue...94
GettingMultipleRows...94
UsingtheQueryBuilderandQueryObjects...94
GettingaLimitedRangeofResults...95
GettingResultsforPagedDisplay...96
OtherCommonQueries...97
InsertsandUpdateswithdrupal_write_record()...98
TheSchemaAPI...99
UsingModule.installFiles...100
CreatingTables...100
UsingtheSchemaModule...102
DeclaringaSpecificColumnTypewithmysql_type...106
MaintainingTables...108
DeletingTablesonUninstall...109
ChangingExistingSchemaswithhook_schema_alter()...110
ModifyingOtherModules’Querieswithhook_query_alter()...111
ConnectingtoMultipleDatabasesWithinDrupal...112
UsingaTemporaryTable...113
WritingYourOwnDatabaseDriver...114
Summary...114
■
Chapter6:WorkingwithUsers...115
The$userObject...115
TestingIfaUserIsLoggedIn...118
Introductiontouserhooks...118
Understandinghook_user_view($account,$view_mode)...120
TheUserRegistrationProcess...122
Usingprofile.moduletoCollectUserInformation...125
TheLoginProcess...125
AddingDatatothe$userObjectatLoadTime...127
ProvidingUserInformationCategories...129
ExternalLogin...130
Summary...135
■
Chapter7:WorkingwithNodes...137
SoWhatExactlyIsaNode?...137
NotEverythingIsaNode...140
CreatingaNodeModule...140
Creatingthe.installFile...140
■ CONTENTS
Creatingthe.moduleFile...143
ProvidingInformationAboutOurNodeType...144
ModifyingtheMenuCallback...145
DefiningNode-Type–SpecificPermissionswithhook_permission()...146
LimitingAccesstoaNodeTypewithhook__node_access(). ...147
CustomizingtheNodeFormforOurNodeType. ...148
ValidatingFieldswithhook_validate()...149
SavingOurDatawithhook_insert()...149
KeepingDataCurrentwithhook_update()...150
CleaningUpwithhook_delete()...150
ModifyingNodesofOurTypewithhook_load()...151
Usinghook_view()...151
ManipulatingNodesThatAreNotOurTypewithhook_node_xxxxx(). ...153
HowNodesAreStored...155
CreatingaNodeTypewithCustomContentTypes...157
RestrictingAccesstoNodes...157
DefiningNodeGrants...157
TheNodeAccessProcess...159
Summary...161
■
Chapter8:WorkingwithFields. ...163
CreatingContentTypes...163
AddingFieldstoaContentType. ...165
CreatingaCustomField...169
AddingFieldsProgrammatically. ...181
■
Chapter9:TheThemeSystem...185
Themes...185
InstallinganOff-the-ShelfTheme...185
BuildingaTheme...186
The.infoFile...194
AddingRegionstoYourTheme...194
AddingCSSFilestoYourTheme...194
AddingJavaScriptFiles...195
AddingSettingstoYourTheme...195
UnderstandingTemplateFiles...198
TheBigPicture...198
Thehtml.php.tplFile...200
OverridingTemplateFiles...214
OtherTemplateFiles...215
OverridingThemableItems...216
OverridingwithTemplateFiles...219
AddingandManipulatingTemplateVariables...219
UsingtheThemeDeveloperModule...221
Summary...221
■
Chapter10:WorkingwithBlocks...223
WhatIsaBlock?...223
BlockConfigurationOptions...226
BlockPlacement...227
DefiningaBlock...228
UsingtheBlockHooks...229
BuildingaBlock...230
■ CONTENTS
BlockVisibilityExamples...238
DisplayingaBlocktoLogged-InUsersOnly...238
DisplayingaBlocktoAnonymousUsersOnly...238
Summary...238
■
Chapter11:TheFormAPI...239
UnderstandingFormProcessing...239
InitializingtheProcess...241
SettingaToken...241
SettinganID...241
CollectingAllPossibleFormElementDefinitions...241
LookingforaValidationFunction...242
LookingforaSubmitFunction...243
AllowingModulestoAltertheFormBeforeIt’sBuilt...243
BuildingtheForm...243
AllowingFunctionstoAltertheFormAfterIt’sBuilt...243
CheckingIftheFormHasBeenSubmitted...244
FindingaThemeFunctionfortheForm...244
AllowingModulestoModifytheFormBeforeIt’sRendered...244
RenderingtheForm...244
ValidatingtheForm...245
SubmittingtheForm...246
RedirectingtheUser...246
CreatingBasicForms...247
FormProperties...249
FormIDs...249
Fieldsets...250
ThemingForms...253
SpecifyingValidationandSubmissionFunctionswithhook_forms()...257
WritingaValidationFunction...258
FormRebuilding...262
WritingaSubmitFunction...263
ChangingFormswithhook_form_alter()...263
SubmittingFormsProgrammaticallywithdrupal_form_submit()...265
DynamicForms...265
FormAPIProperties...273
PropertiesfortheRootoftheForm...273
PropertiesAddedtoAllElements...274
PropertiesAllowedinAllElements...275
FormElements...277
Summary...293
■
Chapter12:ManipulatingUserInput:TheFilterSystem...295
Filters...295
FiltersandTextformats...296
InstallingaFilter...300
KnowingWhentoUseFilters...300
CreatingaCustomFilter...301
Implementinghook_filter_info()...302
TheProcessFunction...302
HelperFunction...303
Summary...305
■
Chapter13:SearchingandIndexingContent...307
BuildingaCustomSearchPage...307
TheDefaultSearchForm...308
TheAdvancedSearchForm...308
■ CONTENTS
UsingtheSearchHTMLIndexer...312
WhentoUsetheIndexer...313
HowtheIndexerWorks...313
Summary...322
■
Chapter14:WorkingwithFiles...323
HowDrupalServesFiles...323
ManagedandUnmanagedDrupalAPIs...323
PublicFiles...325
PrivateFiles...325
PHPSettings...325
MediaHandling...326
UploadField...327
VideoandAudio...328
FileAPI...328
DatabaseSchema...328
CommonTasksandFunctions...329
AuthenticationHooksforDownloading...340
Summary...342
■
Chapter15:WorkingwithTaxonomy...343
TheStructureofTaxonomy...343
CreatingaVocabulary...343
CreatingTerms...344
AssigningaVocabularytoaContentType...344
KindsofTaxonomy...345
Flat...346
Hierarchical...346
ViewingContentbyTerm...349
UsingANDandORinURLs...349
SpecifyingDepthforHierarchicalVocabularies...349
AutomaticRSSFeeds...350
StoringTaxonomies...351
Module-BasedVocabularies...352
CreatingaModule-BasedVocabulary...352
KeepingInformedofVocabularyChangeswithTaxonomyHooks...352
CommonTasks...354
DisplayingTaxonomyTermsAssociatedwithaNode...354
BuildingYourOwnTaxonomyQueries...355
Usingtaxonomy_select_nodes()...355
TaxonomyFunctions...355
RetrievingInformationAboutVocabularies...355
Adding,Modifying,andDeletingVocabularies...356
RetrievingInformationAboutTerms...357
Adding,Modifying,andDeletingTerms...358
RetrievingInformationAboutTermHierarchy...359
FindingNodeswithCertainTerms...362
AdditionalResources...363
Summary...363
■
Chapter16:Caching...365
KnowingWhentoCache...365
HowCachingWorks...366
HowCachingIsUsedWithinDrupalCore...368
MenuSystem...368
CachingFilteredText...368
■ CONTENTS
Blocks...372
UsingtheCacheAPI...374
Summary...378
■
Chapter17:Sessions...379
WhatAreSessions?...379
Usage...379
Session-RelatedSettings...381
In.htaccess...381
Insettings.php...381
Inbootstrap.inc...382
RequiringCookies...383
Storage...383
SessionLifeCycle...384
SessionConversations...385
FirstVisit...386
SecondVisit...386
UserwithanAccount...386
CommonTasks...386
ChangingtheLengthofTimeBeforeaCookieExpires...386
ChangingtheNameoftheSession...387
StoringDataintheSession...387
Summary...388
■
Chapter18:UsingjQuery...389
WhatIsjQuery?...389
HowjQueryWorks...391
UsingaCSSIDSelector...391
UsingaCSSClassSelector...392
jQueryWithinDrupal...392
YourFirstjQueryCode...393
TargetinganElementbyID...396
MethodChaining...396
AddingorRemovingaClass...397
WrappingExistingElements...397
ChangingValuesofCSSElements...398
WheretoPutJavaScript...399
OverridableJavaScript...402
BuildingajQueryVotingWidget...405
BuildingtheModule...407
UsingDrupal.behaviors...414
WaystoExtendThisModule...415
Compatibility...415
NextSteps...415
Summary...416
■
Chapter19:LocalizationandTranslation...417
EnablingtheLocaleModule...417
UserInterfaceTranslation...417
Strings...417
TranslatingStringswitht()...418
■ CONTENTS
StartingaNewTranslation...429
Generating.potFileswithTranslationTemplateExtractor...429
Creatinga.potFileforYourModule...430
Creating.potFilesforanEntireSite...431
InstallingaLanguageTranslation...432
SettingUpaTranslationatInstallTime...432
InstallingaTranslationonanExistingSite...433
Right-to-LeftLanguageSupport...434
LanguageNegotiation...435
Default...436
PathPrefixOnly...438
PathPrefixwithLanguageFallback...440
URLOnly...441
ContentTranslation...442
IntroducingtheContentTranslationModule...442
MultilingualSupport...442
MultilingualSupportwithTranslation...444
Localization-andTranslation-RelatedFiles...447
AdditionalResources...448
Summary...449
■
Chapter20:XML-RPC...451
WhatIsXML-RPC?...451
PrerequisitesforXML-RPC...451
XML-RPCClients...452
XML-RPCClientExample:GettingtheTime...452
XML-RPCClientExample:GettingtheNameofaState...453
ASimpleXML-RPCServer...457
MappingYourMethodwithhook_xmlrpc()...458
AutomaticParameterTypeValidationwithhook_xmlrpc()...459
Built-InXML-RPCMethods...461
system.listMethods...461
system.methodSignature...462
system.methodHelp...462
system.getCapabilities...462
system.multiCall...463
Summary...463
■
Chapter21:WritingSecureCode...465
HandlingUserInput...465
ThinkingAboutDataTypes...465
Usingcheck_plain()andt()toSanitizeOutput...468
Usingfilter_xss()toPreventCross-SiteScriptingAttacks...470
Usingfilter_xss_admin()...472
HandlingURLsSecurely...472
MakingQueriesSecurewithdb_query()...473
KeepingPrivateDataPrivatewithhook_query_alter()...476
DynamicQueries...477
PermissionsandPageCallbacks...477
Cross-SiteRequestForgeries(CSRF)...478
FileSecurity...478
FilePermissions...479
ProtectedFiles...479
FileUploads...480
FilenamesandPaths...480
■ CONTENTS
FilesforProductionEnvironments...482
SSLSupport. ...482
Stand-AlonePHP. ...483
AJAXSecurity,a.k.a.RequestReplayAttack. ...485
FormAPISecurity. ...485
ProtectingtheSuperuserAccount...486
Summary. ...486
■
Chapter22:DevelopmentBestPractices. ...487
ExampleURLs...496
NamingConventions...496
CheckingYourCodingStylewithCoderModule...496
FindingYourWayAroundCodewithgrep...497
Summary...498
■
Chapter23:OptimizingDrupal...499
CachingIstheKeytoDrupalPerformance...499
OptimizingPHP...501
SettingPHPOpcodeCacheFileto/dev/zero...502
PHPProcessPoolSettings...502
TuningApache...503
mod_expires...503
MovingDirectivesfrom.htaccesstohttpd.conf...504
MPMPreforkvs.ApacheMPMWorker...504
BalancingtheApachePoolSize...505
DecreasingApacheTimeout...505
DisablingUnusedApacheModules...506
UsingNginxInsteadofApache...506
UsingPressflow...506
Varnish...506
■ CONTENTS
■
Chapter24:InstallationProfiles...525
CreatingaNewInstallationProfile...525
Theenhanced.infoFile...526
Theenhanced.profileFile...527
Theenhanced.installFile...527
Usinghook_install_tasksandhook_install_tasks_alter...543
Summary...544
■
Chapter25:Testing...545
SettingUptheTestEnvironment...545
HowTestsAreDefined...550
TestFunctions...556
TestAssertions...560
Summary...563
■
AppendixA:DatabaseTableReference...565
■
AppendixB:Resources...623
■ CONTENTS
MailingLists...625
Development...625
Themes...625
Translations...625
UserGroupsandInterestGroups...625
InternetRelayChat...625
NorthAmerica...627
Europe...627
Asia...628
LatinAmerica/Caribbean...629
Oceania...629
Africa...629
Videocasts...629
Weblogs...629
Conferences...630
Contribute...630
Foreword
Fouryearsago,Iwrotetheforewordforthefirsteditionofthisbook.Whatwasmissingatthattimewas adeveloperbookforDrupal.Sincethen,ProDrupalDevelopmenthasmadeanincrediblecontribution toDrupal’ssteadygrowth.Idon’tthinkIknowasingleDrupaldeveloperwhodoesn’townacopyofthe
ProDrupalDevelopmentbook.
Drupal,throughitsopensourcenature,hasbecomemuchgreaterthanIeverimagineditwould. Whatdidn’tchangeistheDrupaldevelopercommunity’shealthydesiretoinnovate,torespondtothe ever-changinglandscapeofwebdevelopment,andtoprovidewebdevelopersanalmostinfiniteamount offlexibility.ChangeisaconstantintheDrupalcommunityandkeytooursuccess.
AlotofthesuccessofDrupaltodaycanbeattributedtoDrupal6.However,fromthedaythat Drupal6wasreleasedalmostthreeyearsago,we’vebeenworkingreallyhardonDrupal7.Morethan 800individualcontributorshavepatchesincludedinDrupal7core.Drupal7willfeaturesomeofthe biggestarchitecturalchangesinthehistoryofDrupal,willshipwithmanyAPIimprovements,andwill beabletopowerbiggersitesthaneverbefore.ThenetresultisthatDrupal7isanevenbetterweb applicationdevelopmentplatformthanDrupal6,anditwillfuelalotofDrupal’sgrowthoverthenext years.
AllthesechangesalsomeanthatthepreviousProDrupalDevelopmentbookswentoutofdate. Fortunately,thethirdeditionofthisbookfixesallthat.Thisbookcoversallofthecapabilitiesand developerfacilitiesinDrupal7,andprovidesdeepinsightintotheinnerworkingsanddesignchoices behindDrupal7.
ArmedwiththisbookandacopyofDrupal’ssourcecode,youhaveeverythingyouneedtobecome aDrupalexpert.If,alongtheway,youhavefiguredouthowtodosomethingbetter,withfewerlinesof codeormoreelegantlyandfasterthanbefore,getinvolvedandhelpusmakeDrupalevenbetter.I’dlove toreviewandcommityourDrupalcorepatches,andI’msuremanyoftheothercontributorswouldtoo.
About the Authors
■ToddTomlinsonisthevicepresidentofeGovernmentSolutionsat
ServerLogicCorporationinPortland,Oregon.Todd’sfocusoverthepast15 yearshasbeenondesigning,developing,deploying,andsupportingcomplex websolutionsforpublicandprivatesectorclientsallaroundtheworld.Hehas beenusingDrupalastheprimaryplatformforcreatingbeautifulandfeature-richsitessuchashttp://arapahoelibraries.org/ald/.
PriortoServerLogic,ToddwastheseniordirectorofeBusinessStrategic ServicesforOracleCorporation,wherehehelpedOracle’slargestclientsdevelop theirstrategicplansforleveragingtheWebasacorecomponentoftheir
business.HeisalsotheformervicepresidentofInternetSolutionsfor ClaremontTechnologyGroup,vicepresidentandCTOofEmeraldSolutions, managingdirectorforCNFVentures,andaseniormanagerwithAndersen Consulting/Accenture.ToddhasaBSincomputerscienceandanMBA,andhe isinthedissertationphaseforhisPhD.
Todd’spassionforDrupalisevidentinhisobsessionwithevangelizingabouttheplatformandhis enthusiasmwhenspeakingwithclientsaboutthepossibilitiesofwhattheycanaccomplishusing Drupal.Ifyouwanttoseesomeoneliterally“lightup,”stophimonthestreetandaskhim,“Whatis Drupalandwhatcanitdoforme?”HeisalsotheauthorofApress’sBeginningDrupal7.
■JohnK.VanDykbeganhisworkwithcomputersonablackBellandHowell AppleIIbyprintingoutandporingovertheBASICcodeforLittleBrickOutin ordertoincreasethepaddlewidth.Later,hemanipulatedtimingloopsin assemblytogivePac-Manalargertimeslicethantheghosts.Beforediscovering Drupal,JohnwasinvolvedwiththeUserLandFrontiercommunityandused Plonebeforewritinghisowncontentmanagementsystem(withMattWestgate) usingRuby.
JohnisaseniorwebarchitectatLullabot,aDrupaleducationand consultingfirm.Beforethat,Johnwasasystemsanalystandadjunctassistant professorintheentomologydepartmentatIowaStateUniversityofScienceand Technology.Hismaster’sthesisfocusedoncoldtoleranceofdeerticks,andhis doctoraldissertationwasontheeffectivenessofphotographicallycreatedthree-dimensionalvirtual insectsonundergraduatelearning.
About the Technical Reviewers
■JoshuaBrauerjumpedontotheWorldWideWebasanaspiringtechnical
journalismstudentworkingwithcontentmanagementsystemsin1995.Since aboutDrupalcanbefoundonlineathttp://joshuabrauer.com.
AsaDrupalistatAcquia,Joshuaworkswithcustomersfromsmallsitesto
■RobertDouglassistheseniorDrupaladvisoratAcquia,Inc.,apermanent
memberoftheDrupalAssociation,andafoundingmemberofDieDrupal-Initiative,Germany’sDrupal-orientednonprofit.Heisactiveasamodule maintainer,corecontributor,andspeakeratvariousDrupaleventsand conferences.HisApressprojectsincludeBuildingOnlineCommunitieswith Drupal,phpBB,andWordPress(author,2005),ProDrupalDevelopment (technicalreviewer,2007),andProDrupalDevelopment,SecondEdition
(technicalreviewer,2008).
Acknowledgments
Beth,foryournever-endingsupport,encouragement,love,andlaughter—thankyouforbringingback theabilitytodreambigaboutthefuture.
Mydaughters,Anna,Alissa,andEmma,forgivingupcountlesshoursoftimewithDadwhileIwrotethe book.
Myparents,forgivingmethetoolsIneededtoembarkonthejourneysthatI’vetraveled.
Mygrandmother,forsparkingthefiretobecomeanauthor.
Dries,withoutyourvisionandpassionfortheplatform,therewouldn’tbeaProDrupalDevelopment book.
TheAquiateam,forjumpinginandlendingyoursupportwhileItackledthetoughsectionsofthebook Webchick(a.k.a.AngieByron),foryourdedicationtotheplatformandyourrelentlesseffortstolaunch Drupal7.
Thethousandsofdeveloperswhohavecontributedtotheplatformtomakeitwhatitistoday.
Myclients,forembracingthetechnologyandsharingtheexcitementoverwhatitcando.
Introduction
Initsrelativelyshortlife,DrupalhashadatremendousimpactonthelandscapeoftheInternet.Asa webcontentmanagementsystem,Drupalhasenabledthecreationoffeature-andcontent-richweb sitesfororganizationslargeandsmall.Asawebapplicationframework,Drupalischangingthewaythat peoplethinkaboutwebapplicationdevelopment.WhenIexperiencedthepoweroftheDrupalplatform forthefirsttime,Iknewthatitwassomethingmorethanjustanothercontentmanagementsolution. WhenIsawhoweasilyandquicklyIcouldbuildfeature-richwebsites,Ishiftedgearsandfocusedmy entirecareeraroundDrupal.
I’moftenaskedthequestion,“WhatisDrupal?”TheshortanswerisDrupalisanopensourceweb contentmanagementsystemthatallowsyoutoquicklyandeasilycreatesimpletocomplexwebsites thatspaneverythingfromasimpleblogtoacorporatewebsite,asocialnetworkingwebsite,orvirtually anythingyoucandreamup.WhatyoucanbuildwithDrupalislimitedonlytoyourimagination,the timeyouhavetospendwiththeplatform,andyourknowledgeaboutDrupal’scapabilities—whichisthe impetusbehindthisbook.
Asanopensourceplatform,Drupal’scommunityisconstantlyimprovingtheplatformand extendingthefunctionalityofthecoreplatformbycreatingnewandexcitingadd-onmodules.Ifthere’s anewconceptcreatedontheWeb,it’slikelythattherewillbeanewDrupalmodulethatenablesthat conceptinamatterofdays.It’sthecommunitybehindtheplatformthatmakesDrupalwhatitistoday, andwhatitwillbecomeinthefuture.I’llshowyouhowtoleveragethefeaturescontributedbythe community,makingiteasyforyoutobuildincrediblesolutionswithminimaleffort.
Theveryactofpickingupthisbookisthefirststepinyourjourneydownthepathoflearninghowto useDrupal.Ifyouwillwalkwithmethroughtheentirebook,you’llhavetheknowledgeandexperience tobuildcomplexandpowerfulDrupal-basedwebsites.You’llalsohavethefoundationnecessaryto movebeyondthebasics,expandingontheconceptsIcoverinthisbook.
LearningDrupalislikelearningeverynewtechnology.Therewillbebumpsandhurdlesthatcause youtostepbackandscratchyourhead.Ihopethebookhelpssmooththebumpsandprovidesyouwith enoughinformationtoeasilyjumpoverthosehurdles.IlookforwardtoseeingyourworksontheWeb andhopetobumpintoyouatanupcomingDrupalCon.
■ ■ ■
How Drupal Works
Inthischapter,I’llgiveyouanoverviewofDrupal.Detailsonhoweachpartofthesystemworkswillbe providedinlaterchapters.Here,we’llcoverthetechnologystackonwhichDrupalruns,thelayoutofthe filesthatmakeupDrupal,andthevariousconceptualtermsthatDrupaluses,suchasnodes,hooks, blocks,andthemes.
WhatIsDrupal?
Drupalisusedtobuildwebsites.It’sahighlymodular,opensourcewebcontentmanagement frameworkwithanemphasisoncollaboration.Itisextensible,standards-compliant,andstrivesfor cleancodeandasmallfootprint.Drupalshipswithbasiccorefunctionality,andadditionalfunctionality isgainedbyenablingbuilt-inorthird-partymodules.Drupalisdesignedtobecustomized,but
customizationisdonebyoverridingthecoreorbyaddingmodules,notbymodifyingthecodeinthe core.Drupal’sdesignalsosuccessfullyseparatescontentmanagementfromcontentpresentation.
DrupalcanbeusedtobuildanInternetportal;apersonal,departmental,orcorporatewebsite;an e-commercesite;aresourcedirectory;anonlinenewspaper;asocialnetworkingsite;animagegallery; anintranet;andvirtuallyanyothertypeofwebsitethatyoucanimaginecreating.
AdedicatedsecurityteamstrivestokeepDrupalsecurebyrespondingtothreatsandissuing securityupdates.AnonprofitorganizationcalledtheDrupalAssociationsupportsDrupalbyimproving thedrupal.orgwebsiteinfrastructureandorganizingDrupalconferencesandevents.Andathriving onlinecommunityofusers,siteadministrators,designers,andwebdevelopersworkshardtocontinually improvethesoftware;seehttp://drupal.organdhttp://groups.drupal.org.
TechnologyStack
Drupal’sdesigngoalsincludebothbeingabletorunwelloninexpensivewebhostingaccounts andbeingabletoscaleuptomassivedistributedsites.Theformergoalmeansusingthemost
CHAPTER1■HOWDRUPALWORKS
Figure1-1.Drupal’stechnologystack
TheoperatingsystemisatsuchalowlevelinthestackthatDrupaldoesnotcaremuchaboutit. DrupalrunssuccessfullyonanyoperatingsystemthatsupportsPHP.
ThewebservermostwidelyusedwithDrupalisApache,thoughotherwebservers(including MicrosoftIIS)maybeused.BecauseofDrupal’slonghistorywithApache,Drupalshipswith.htaccess filesthatsecuretheDrupalinstallation.CleanURLs—thatis,thosedevoidofquestionmarks,
ampersands,orotherstrangecharacters—areachievedusingApache’smod_rewritecomponent.Thisis particularlyimportantbecausewhenmigratingfromanothercontentmanagementsystemorfrom staticfiles,theURLsofthecontentneednotchange,andunchangingURIsarecool,accordingtoTim Berners-Lee(http://www.w3.org/Provider/Style/URI).CleanURLsareavailableonotherwebservers byusingthewebserver’sURLrewritingcapabilities.
Drupalinterfaceswiththenextlayerofthestack(thedatabase)throughalightweightdatabase abstractionlayer,whichwastotallyrewritteninDrupal7.ThedatabaseinterfaceprovidesanAPIbased onPHPdataobject(orPDO)andallowsDrupaltosupportanydatabasethatsupportsPHP.Themost populardatabasesincludeMySQLandPostgreSQL.InDrupal7,SQLiteisnowalsosupported.
DrupaliswritteninPHP.AllcoreDrupalcodeadherestostrictcodingstandards
(http://drupal.org/nodes/318)andundergoesthoroughreviewthroughtheopensourceprocess.For Drupal,theeasylearningcurveofPHPmeansthatthereisalowbarriertoentryforcontributorswhoare juststartingout,andthereviewprocessensuresthiseaseofaccesscomeswithoutsacrificingqualityin theendproduct.Andthefeedbackbeginnersreceivefromthecommunityhelpstoimprovetheirskills. ForDrupal7,therequiredversionofPHPis5.2.
Core
AlightweightframeworkmakesuptheDrupalcore.ThisiswhatyougetwhenyoudownloadDrupal fromdrupal.org.Thecoreisresponsibleforprovidingthebasicfunctionalitythatwillbeusedto supportotherpartsofthesystem.
Figure1-2.AnoverviewoftheDrupalcore(notallcorefunctionalityisshown)
Thecorealsoincludesthebasicfunctionalbuildingblocksformostwebsites,includingfeed aggregation,blogging,polls,andforums.
AdministrativeInterface
TheadministrativeinterfaceinDrupalistightlyintegratedwiththerestofthesite.Alladministrative functionsareeasilyaccessiblethroughanadministrativemenuthatappearsatthetopofthepagewhen youareloggedinasasiteadministrator.
Modules
CHAPTER1■HOWDRUPALWORKS
ModulescanextendDrupalbyaddingnewcontenttypessuchasrecipes,blogposts,orfiles,and behaviorssuchase-mailnotification,peer-to-peerpublishing,andaggregation.Drupalmakesuseofthe inversionofcontroldesignpattern,inwhichmodularfunctionalityiscalledbytheframeworkatthe appropriatetime.Theseopportunitiesformodulestodotheirthingarecalledhooks.
Hooks
HookscanbethoughtofasinternalDrupalevents.Theyarealsocalledcallbacks,butbecausetheyare constructedbyfunction-namingconventionsandnotbyregisteringwithalistener,theyarenottruly beingcalledback.Hooksallowmodulesto“hookinto”whatishappeningintherestofDrupal.
SupposeauserlogsintoyourDrupalwebsite.Atthetimetheuserlogsin,Drupalfires
hook_user_login.Thatmeansthatanyfunctionnamedaccordingtotheconventionmodule nameplus
hook namewillbecalled.Forexample,comment_user_login()inthecommentmodule,
locale_user_login()inthelocalemodule,node_user_login()inthenodemodule,andanyother similarlynamedfunctionswillbecalled.Ifyouweretowriteacustommodulecalledspammy.moduleand includeafunctioncalledspammy_user_login()thatsentane-mailtotheuser,yourfunctionwouldbe calledtoo,andthehaplessuserwouldreceiveanunsolicitede-mailateverylogin.
ThemostcommonwaytotapintoDrupal’scorefunctionalityisthroughtheimplementationof hooksinmodules.
■
Tip
FormoredetailsaboutthehooksDrupalsupports,seetheonlinedocumentationat
http://api.drupal.org/api/7
,andlookunderComponentsofDrupal,then“Modulesystem(Drupalhooks).”
Themes
Whencreatingawebpagetosendtoabrowser,therearereallytwomainconcerns:assemblingthe appropriatedataandmarkingupthedatafortheWeb.InDrupal,thethemelayerisresponsiblefor creatingtheHTML(orJSON,XML,etc.)thatthebrowserwillreceive.DrupalusesPHPTemplateasthe primarytemplatingengine,oralternativelyyoucanusetheEasyTemplateSystem(ETS).Most
developersstickwiththestandardtemplatingenginewhenconstructingnewDrupalthemes.The importantthingtorememberisthatDrupalencouragesseparationofcontentandmarkup.
Drupalallowsseveralwaystocustomizeandoverridethelookandfeelofyourwebsite.The simplestwayisbyusingacascadingstylesheet(CSS)tooverrideDrupal’sbuilt-inclassesandIDs. However,ifyouwanttogobeyondthisandcustomizetheactualHTMLoutput,you’llfinditeasytodo. Drupal’stemplatefilesconsistofstandardHTMLandPHP.Additionally,eachdynamicpartofaDrupal page,suchasalistorbreadcrumbtrail,canbeoverriddensimplybydeclaringafunctionwithan appropriatename.ThenDrupalwilluseyourfunctioninsteadtocreatethatpartofthepage.
CHAPTER1■HOWDRUPALWORKS
Nodes
ContenttypesinDrupalarederivedfromasinglebasetypereferredtoasanode.Whetherit’sablog entry,arecipe,orevenaprojecttask,theunderlyingdatastructureisthesame.Thegeniusbehindthis approachisinitsextensibility.Moduledeveloperscanaddfeatureslikeratings,comments,file
attachments,geolocationinformation,andsoforthfornodesingeneralwithoutworryingaboutwhether thenodetypeisblog,recipe,orsoon.Thesiteadministratorcanthenmixandmatchfunctionalityby contenttype.Forexample,theadministratormaychoosetoenablecommentsonblogsbutnotrecipes orenablefileuploadsforprojecttasksonly.
Nodesalsocontainabasesetofbehavioralpropertiesthatallothercontenttypesinherit.Anynode canbepromotedtothefrontpageofthewebsite,publishedorunpublished,orevensearched.And becauseofthisuniformstructure,theadministrativeinterfaceisabletoofferabatcheditingscreenfor workingwithnodes.
Fields
ContentinDrupaliscomposedofindividualfields.Anodetitleisafield,asisthenodebody.Youcan usefieldsinDrupaltoconstructanycontenttypethatyoucanthinkof—forexample,anEvent.Ifyou thinkaboutanEvent,ittypicallycontainsatitle,adescription(orbody),astartdate,astarttime,a duration,alocation,andpossiblyalinktoregisterfortheevent.Eachofthoseelementsrepresentsa field.InDrupalwehavetheabilitytocreatecontenttypesusingfields—eitherprogrammaticallyby creatingamodule,orthroughtheDrupaladministrativeinterfacebycreatinganewcontenttypeand assigningfieldsthroughtheuserinterface.ThegreatnewsisthattheFieldAPImakesitextremelyeasy tocreatesimpletocomplexcontenttypeswithverylittleprogramming.
Blocks
Ablockisinformationthatcanbeenabledordisabledinaspecificlocationonyourwebsite’stemplate. Forexample,ablockmightdisplaythenumberofcurrentactiveusersonyoursite.Youmighthavea blockcontaininglinkstothemostpopularcontentonthesite,oralistofupcomingevents.Blocksare typicallyplacedinatemplate’ssidebar,header,orfooter.Blockscanbesettodisplayonnodesofa certaintype,onlyonthefrontpage,oraccordingtoothercriteria.
Oftenblocksareusedtopresentinformationthatiscustomizedtothecurrentuser.Forexample, theuserblockcontainsonlylinkstotheadministrativeareasofthesitetowhichthecurrentuserhas access,suchasthe“Myaccount”page.Regionswhereblocksmayappear(suchastheheader,footer,or rightorleftsidebar)aredefinedinasite’stheme;placementandvisibilityofblockswithinthoseregions ismanagedthroughtheweb-basedadministrativeinterface.
FileLayout
Figure1-4.ThedefaultfolderstructureofaDrupalinstallation
Detailsabouteachelementinthefolderstructurefollow:
• TheincludesfoldercontainslibrariesofcommonfunctionsthatDrupaluses.
• ThemiscfolderstoresJavaScriptandmiscellaneousiconsandimagesavailableto astockDrupalinstallation.
CHAPTER1■HOWDRUPALWORKS
• Theprofilesfoldercontainsdifferentinstallationprofilesforasite.Ifthereare otherprofilesbesidesthedefaultprofileinthissubdirectory,Drupalwillaskyou whichprofileyouwanttoinstallwhenfirstinstallingyourDrupalsite.Themain purposeofaninstallationprofileistoenablecertaincoreandcontributed modulesautomatically.Anexamplewouldbeane-commerceprofilethat automaticallysetsupDrupalasane-commerceplatform.
• Thescriptsfoldercontainsscriptsforcheckingsyntax,cleaningupcode,running Drupalfromthecommandline,handlingspecialcaseswithcron, and running the test suites (new in Drupal 7).ThisfolderisnotusedwithintheDrupal requestlifecycle;theseareshellandPerlutilityscripts.
• Thesitesdirectory(seeFigure1-5)containsyourmodificationstoDrupalinthe formofsettings,modules,andthemes.WhenyouaddmodulestoDrupalfromthe contributedmodulesrepositoryorbywritingyourown,theygointo
-sites/all/modules.ThiskeepsallyourDrupalmodificationswithinasingle folder.Insidethesitesdirectorywillbeasubdirectorynameddefaultthatholds thedefaultconfigurationfileforyourDrupalsite—default.settings.php.The Drupalinstallerwillmodifytheseoriginalsettingsbasedontheinformationyou provideandwriteasettings.phpfileforyoursite.Thedefaultdirectoryistypically copiedandrenamedtotheURLofyoursitebythepersondeployingthesite,so yourfinalsettingsfilewouldbeatsites/www.example.com/settings.php.
• Thesites/default/filesfolderisincludedinthebaseinstallationofDrupalby default.Itisneededtostoreanyfilesthatareuploadedtoyoursiteand
sites/default/files,asites/default/privatedirectorymaybecreatedfor storingfilesthataresensitiveinnatureandshouldn’tbedisplayedunlessthesite visitorhasthepropercredentials.Youcreatetheprivatefilesdirectoryby navigatingtoConfiguration>FileSystemandenteringthedirectorywhereyou wantprivatefilestoresideinthetextfieldtitledPrivatefilesystempath.
• ThethemesfoldercontainsthetemplateenginesanddefaultthemesforDrupal. Additionalthemesyoudownloadorcreateshouldnotgohere;theygointo
sites/all/themes.
• cron.phpisusedforexecutingperiodictasks,suchaspruningdatabasetablesand calculatingstatistics.
• index.phpisthemainentrypointforservingrequests.
• install.phpisthemainentrypointfortheDrupalinstaller.
• update.phpupdatesthedatabaseschemaafteraDrupalversionupgrade.
• xmlrpc.phpreceivesXML-RPCrequestsandmaybesafelydeletedfrom deploymentsthatdonotintendtoreceiveXML-RPCrequests.
• robots.txtisadefaultimplementationoftherobotexclusionstandard.
• authorize.phpisanadministrativescriptforrunningauthorizedfileoperations— forexample,downloadinganinstallinganewthemeormodulefromDrupal.org. Otherfilesnotlistedherearedocumentationfiles.
Figure1-5.ThesitesfoldercanstoreallyourDrupalmodifications.
ServingaRequest
HavingaconceptualframeworkofwhathappenswhenarequestisreceivedbyDrupalishelpful,sothis sectionprovidesaquickwalk-through.Ifyouwanttotraceityourself,useagooddebugger,andstartat
index.php,whichiswhereDrupalreceivesmostofitsrequests.Thesequenceoutlinedinthissection mayseemcomplexfordisplayingasimplewebpage,butitisrifewithflexibility.
TheWebServer’sRole
Drupalrunsbehindawebserver,typicallyApache.IfthewebserverrespectsDrupal’s.htaccessfile, somePHPsettingsareinitialized,andtheURLisexamined.AlmostallcallstoDrupalgothrough
index.php.Forexample,acalltohttp://example.com/foo/barundergoesthefollowingprocess:
1. Themod_rewriteruleinDrupal’s.htaccessfilelooksattheincomingURLand separatesthebaseURLfromthepath.Inourexample,thepathisfoo/bar.
CHAPTER1■HOWDRUPALWORKS
3. TheresultingURLishttp://example.com/index.php?q=foo/bar.
4. Drupaltreatsfoo/barastheinternalDrupalpath,andprocessingbeginsin
index.php.
Asaresultofthisprocess,Drupaltreatshttp://example.com/index.php?q=foo/barand
http://example.com/foo/barexactlythesameway,becauseinternallythepathisthesameinbothcases. ThisenablesDrupaltouseURLswithoutfunny-lookingcharactersinthem.TheseURLsarereferredto ascleanURLs.
Inalternatewebservers,suchasMicrosoftIIS,cleanURLscanbeachievedusingaWindows InternetServerApplicationProgrammingInterface(ISAPI)modulesuchasISAPIRewrite.IISversion7 andlatersupportsrewritingdirectly.IfyouarerunningyoursiteonIIS7orlater,you’llwanttocheck outtheweb.configfilethatenablescleanURLsandprotectspryingeyesfromfilesthatwereallydon’t wantthemtohaveaccessto,like.install,.module,.test,.theme,.profile,.info,and.incfiles.
TheBootstrapProcess
Drupalbootstrapsitselfoneveryrequestbygoingthroughaseriesofbootstrapphases.Thesephasesare definedinbootstrap.incandproceedasdescribedinTable1-1.
Table1-1.BootstrapPhases
Phase
Purpose
Configuration Setsglobalvariablesusedthroughoutthebootstrapprocess.
Database Initializesthedatabasesystemandregistersautoloadfunctions.
Variables Loadssystemvariablesandallenabledbootstrapmodules.
Session Initializessessionhandling.
PageHeader Invokeshook_boot(),initializesthelockingsystem,andsendsthedefaultHTTP headers.
Language Initializesallthedefinedlanguagetypes.
Full Thefinalphase:Drupalisfullyloadedbynow.Thisphasevalidatesandfixestheinput data.
ProcessingaRequest
ThemingtheData
Theminginvolvestransformingthedatathathasbeenretrieved,manipulated,orcreatedintoHTML(or XMLorotheroutputformat).Drupalwillusethethemetheadministratorhasselectedtogivetheweb pagethecorrectlookandfeel.Theresultingoutputisthensenttothewebbrowser(orotherHTTP client).
Summary
■ ■ ■
Writing a Module
ModulesarethebasicbuildingblocksthatformthefoundationofDrupalandarethemechanismsfor extendingthefunctionalityprovidedbytheoff-the-shelfversionofDrupal,alsoknownasDrupalcore.I oftenexplaintothosewhoareunfamiliarwithDrupalthatmodulesarelikeLegobuildingblocks.They fittogetherperfectlybyfollowingapredefinedsetofguidelines,andwithacombinationofmodules,you canbuildrichandcomplexsolutions.
TherearetwogeneralcategoriesofDrupalmodules—coreandcontributed.Coremodulesarethose thatareshippedwithDrupalandincludemodulessuchaspolls,menus,taxonomy,search,feed
aggregator,andforums.Contributedmodulesareallofthemodulescreatedbythecommunitythat extendandenhancethefunctionalfootprintofDrupalcore.Thereareliterallythousandsofcontributed modulesavailablefordownloadathttp://drupal.org/project/modulesandspaneverythingfrom simplesingletaskmodules,suchasdisplayingthecurrentdateandtime,tocomplexsolutions,suchas ane-commercestorefront.
Inthischapter,Iwillshowyouhowtobuildacustommodulefromscratch.Asyoubuildthe module,you’lllearnaboutthestandardstowhichmodulesmustadhere.Ineedarealisticgoal,solet’s focusonthereal-worldproblemofannotation.WhenlookingthroughthepagesofaDrupalwebsite, youmaywanttowriteanoteaboutthatpage.WecoulduseDrupal’scommentsfeaturetoaccomplish this,butcommentsaretypicallyviewablebyanyonevisitingthesite,orauthenticatedusers.
Annotations,ontheotherhand,areviewableonlybythenode’sauthor.
CreatingtheFiles
Thefirstthingwearegoingtodoistochooseanameforthemodule.Thename“annotate”seems appropriate—it’sshortanddescriptive.Next,Ineedaplacetoputthemodule.Contributedandcustom modulesarestoredinthe/sites/all/modulesdirectory,witheachmodulestoredinitsowndirectory thatusesthesamenameasthemodule.
■
Note
Drupalcoremodulesarestoredinthe
/modulesdirectory—protectingyourcustomandcontributed
CHAPTER2■WRITINGAMODULE
Youmaywishtocreatea/sites/all/modules/customdirectorytoholdanymodulesthatyoucreate fromscratch,makingiteasyforsomeonelookingatyoursitetounderstandwhichmodulesare
contributedmodulesthatweredownloadedfromDrupal.organdwhichmoduleswerecustom-coded forthissite.NextI’llcreateanannotatedirectorywithinthe/sites/all/modules/customdirectoryto holdallofthefilesassociatedwiththeannotatemodule.
ThefirstfileIwillcreateforthenewmoduleistheannotate.infofile.EverymoduleinDrupal7 musthavea.infofile,andthenamemustmatchthenameofthemodule.Fortheannotatemodule,the basicinformationrequiredforDrupaltorecognizethemoduleis
name = Annotate
description = "Allows users to annotate nodes." package = Pro Drupal Development
core = 7.x
files[] = annotate.module files[] = annotate.install files[] = annotate.admin.inc
configure=admin/config/content/annotate/settings
ThestructureofthefileisstandardacrossallDrupal7modules.Thenameelementisusedto thefilesassociatedwiththismodulearetheannotate.moduleandannotate.installfiles.
Wecouldassignoptionalvaluesinadditiontothoselistedpreviously.Here’sanexampleofa modulethatrequiresPHP5.2andisdependentontheforumandtaxonomymodulesbeinginstalledin orderforthismoduletowork.
name = Forum confusion
description = Randomly reassigns replies to different discussion threads. core = 7.x
dependencies[] = forum dependencies[] = taxonomy files[] = forumconfusion.module files[] = forumconfusion.install package = "Evil Bob's Forum BonusPak" php = 5.2
Nowwe’rereadytocreatetheactualmodule.Createafilenamedannotate.moduleinsideyour
<?php
/** * @file
* Lets users add private annotations to nodes. *
* Adds a text field when a node is displayed * so that authenticated users may make notes. */
First,notethecommentstyle.Webeginwith/**,andoneachsucceedingline,weuseasingle asteriskindentedwithonespace(*)and*/onalinebyitselftoendacomment.The@filetoken denotesthatwhatfollowsonthenextlineisadescriptionofwhatthisfiledoes.Thisone-line descriptionisusedsothatapi.module (see http://drupal.org/project/api),Drupal’sautomated documentationextractorandformatter,canfindoutwhatthisfiledoes.Whileyou’reonDrupal.org, alsovisithttp://api.drupal.org.Hereyou’llfinddetaileddocumentationoneveryAPIthatDrupal provides.IsuggestyoutakeamomentandlookaroundthissectionofDrupal.org.It’saninvaluable resourceforthoseofuswhodevelopormodifymodules.
Afterablankline,weaddalongerdescriptionaimedatprogrammerswhowillbeexamining(and nodoubtimproving)ourcode.Notethatweintentionallydonotuseaclosingtag(?>);theseare optionalinPHPand,ifincluded,cancauseproblemswithtrailingwhitespaceinfiles(seehttp:// drupal.org/coding-standards#phptags).
■
Note
Whyarewebeingsopickyabouthoweverythingisstructured?It’sbecausewhenhundredsofpeople
fromaroundtheworldworktogetheronaproject,itsavestimewheneveryonedoesthingsonestandardway.
DetailsofthecodingstylerequiredforDrupalcanbefoundinthe“Codingstandards”sectionofthe
Developingfor
DrupalHandbook
(
http://drupal.org/coding-standards).
Ournextorderofbusinessistodefinesomesettingssothatwecanuseaweb-basedformtochoose whichnodetypestoannotate.Therearetwostepstocomplete.First,we’lldefineapathwherewecan accessoursettings.Then,we’llcreatethesettingsform.Tomakeapath,Ineedtoimplementahook, specificallyhook_menu.