Developers Handbook
WORK IN PROGRESS - This handbook has not been endorsed by community vote at this time.
Koha Developer's Handbook
Welcome to the Koha Developer's Handbook. I hope this handbook will be of assistance to Koha developers new and old. This handbook is meant to be a collections of all the guidelines, best practices, and common techniques used throughout Koha to aid developers in creating consistent code to improve the usability and interoperability of our code submissions.
TODO
Merge:
- https://wiki.koha-community.org/wiki/Developer_handbook
- https://wiki.koha-community.org/wiki/Development_workflow
- https://wiki.koha-community.org/wiki/Sign_off_on_patches
Perl
The Basics
Grandfather clause
If you submit code that fixes existing code that violates those guidelines, QA can still be passed. You don't have to fix everything. If you want to fix more than just your fix, to respect current guidelines, you're welcome of course. But in this case, please do it in a 2nd patch, to have reviewers being able to distinguish easily what's related to your bugfix and what is related to your guidelines-violation fixing.
Example: you submit a patch that does not respect perlcritic after applying the patch. The QA team will check for perlcritic after your patch, and if the patch does not add a new perlcritic violation, it's OK.
Patches submitted before the introduction of a new rule may pass QA even if they do not meet the current coding guideline requirements of the discretion of the QA team member.
Handling disagreements
If a community member has concerns about a a patch that:
- Are specifically about coding style (not stated functionality or regressions in other code)
- Are not covered by an existing coding guideline
then the patch and concern should be brought up on the development mailing list and/or next development IRC meeting so a new coding guideline can be added.
Licence
Each file (scripts & modules) must include the GPL licence statement, typically at the top.
# This file is part of Koha. # # Copyright YEAR YOURNAME-OR-YOUREMPLOYER # # Koha is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # Koha is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Koha; if not, see <http://www.gnu.org/licenses>.
Commit messages
When you submit code to the project (using Git) please write useful commit messages. Detailed guidelines on writing commit messages can be found at the wiki page on Commit messages.
Refactoring code
Don't needlessly refactor code, if it ain't broke, don't fix it!
Don't waste time on changing style from someone else's style to yours!
If you must refactor, make sure that the commit for the refactoring is completely separate from a bugfix.
Bug numbers
Contributors to Koha must reference a bug number with every commit. This helps us determine the purpose of the commit and establishes a more searchable and understandable history. If there is no bug open at bugs.koha-community.org that addresses your contribution, please open one and describe the bug or enhancement. Then, include Bug + the bug number at the beginning of your commit message and set the "Status" field to "Needs Signoff". This helps us keep track of patches that have been contributed but not yet applied.
It is also requested that you attach the patch file to the bug report. This allows others not on the patches list to pull in your code, test, and sign-off. You can use Git bz configuration to quickly and easily attach patches to bug reports.
Syntax Formatting (PERL1)
Use perltidy with its default man perlstyle settings.
To install perltidy on Debian use the following command:
apt-get install perltidy
Avoid tidying entire files without a good reason. Tidying entire files without a good reason may cause the patches from other authors to no longer apply.
The following code in your .vimrc file will allow you to highlight and tidy individual pieces of code via the F2 button:
"define :Tidy command to run perltidy on visual selection || entire buffer" command -range=% -nargs=* Tidy <line1>,<line2>!perltidy "run :Tidy on entire buffer and return cursor to (approximate) original position" fun DoTidy() let Pos = line2byte( line( "." ) ) :Tidy exe "goto " . Pos endfun "shortcut for normal mode to run on entire buffer then return to current line" au Filetype perl nmap <F3> :call DoTidy()<CR> "shortcut for visual mode to run on the the current visual selection" au Filetype perl vmap <F3> :Tidy<CR>
Use Modern::Perl (PERL2)
All scripts and modules should use Modern::Perl to enable the strict and warnings pragmas. All you need to do is to ensure
use Modern::Perl;
is placed at the top of your script or module, after the License information.
Fix warnings (PERL3)
Error and warning messages produced by strict and warnings should be quelled by fixing the code, not by tacking on a no warnings directive.
Check your code with Perlcritic (PERL4)
All Perl submitted must validate perlcritic (level 5, the lowest one).
To install perlcritic on Debian, install the perlcritic package:
apt-get install perlcritic
Then you can check your file with the command:
perlcritic -5 /path/to/your/file.pl
PERL5: Disagreements
In case of a disagreement on coding style, the perlstyle man page will decide for us.
Use pointer syntax for hashrefs
For hash refs, use $myhash->{mykey}
, not $$myhash{mykey}
Indentation (PERL6)
Indentation must be 4 spaces (not tabs, not 2, 6 or 8 spaces) You can fix tabs by running the following commands
git config –global core.whitespace trailing-space,space-before-tab git config --global apply.whitespace fix
(from http://blog.bigballofwax.co.nz/2011/12/15/fixing-whitespace-when-apply-patches-with-git/)
If you want to reindent large amounts of existing code in connection with a bugfix or enhancement, you must do so in a separate patch from the one with code changes (unless the reindent is required due to the addition or removal of an indentation level (for example, an additional if added around a block).
Creating new terminology/definitions (PERL7)
The same vocabulary should be used both internally in source code ( Module names, variable names, etc ) and externally ( javascript, templates, etc ).
Canonical Koha terminology is listed on the Terminology page.
If you are developing a new feature that requires new terminology, please locate and use the matching term defined by ODLIS. If you cannot find an appropriate term via ODLIS, please bring the question to the Koha community via the Koha Developers Mailing List or the next Koha Developers IRC Meeting.
Subroutine and method parameters
Use only needed parameters (PERL8)
If you call a sub and don't use some arguments at the end of the sub, it's OK not to pass undef.
That is
some_subroutine($var1,$var2,undef,undef)
can be written as
some_subroutine($var1,$var2)
Hashrefs for arguments (PERL16)
Rather than passing hashes, which is inefficient, or using lots of positional parameters, subroutines that require multiple arguments should use hashrefs for passing arguments, like so:
sub myroutine { my ($args) = @_; return $args>{index} . $args->{value}; } print myroutine({ 'index' => 'kw', 'value' => 'smith' });
An example of BAD practice would be either of the following:
sub myroutine { my (%args) = @_; ## Don't do this return $args{'index'} . $args{'value'}; } print myroutine('index' => 'kw', 'value' => 'smith');
or
sub myroutine { my ($index, $value) = @_; ## Don't do this either return $index . ' => ' . $value; } print myroutine('kw', 'smith')
Verboten subroutine parameters (PERL10)
Don't pass database handles
Some older subroutines accept a database handle as a parameter ( usually named $dbh
).
If you see this, please submit a patch to remove the parameter from the subroutine and its callers.
Use my $dbh = C4::Context->dbh
instead.
Don't pass environment variable
Some older subroutines accept an environment reference variable as a parameter ( usually name $env
).
If you see this, please submit a patch to remove the parameter from the subroutine and its callers.
Use my $env = C4::Context->userenv
instead.
Scripts
CGI Scripts
CLI Scripts
Modules
Naming subroutines and methods (PERL9)
Module subroutines
For regular Koha modules, all subroutine names must contain a verb and a noun. The verb describing what the sub does and the name describing the object it works on.
All words in a subroutine name should be capitalized.
For example
AddBiblio
creates a new record in the databaseDelItem
deletes an item from the databaseGetAuthority
returns the authority you passed in an id forModBudget
modifies and updates an existing budget
If you are familiar with CRUD you should see there is a one to one mapping. Add = Create, Get = Read, Mod = Update, Del = Delete.
Use these four verbs ( Add, Get, Mod, Del ) whenever possible.
- Add to add (NOT "New" or "Create")
- Mod to modify (NOT "Update")
- Del to delete (NOT "Remove")
- Get to read something (NOT "Read" or "Find")
If the sub works on a single object, it's singular. If it works on more than 1, it's plural. For example:
GetLibrary
would return a single library ( usually an object or a hashref ). GetLibraries
would return many libraries ( usually a collection object, or a list of hashrefs )
Private subroutines should begin with an underscore and contain only lower-case characters ( snake_case ) with fully-spelled out names.
For example, _koha_delete_biblio
will delete a biblio record from the koha biblio table.
Module methods
For Object Oriented modules, all methods should be named in snake_case. Since the variable is already the noun, methods may simply be named as a verb.
For example:
$patron->delete();
For methods that return another object, you need only name the method after the object. Keep plurality in mind when naming the methods. If a method returns one object, it should be singular. If it returns many objects, it should be plural.
For example:
my @checkouts = $patron->checkouts(); my $library = $patron->library();
POD (PERL13)
Koha's modules are documented using POD.
Your POD should include as least the following headings:
- NAME - The name of the module and a very brief description of the type of functions contained in the module.
- SYNOPSIS - This may give an example of how to access the module from a script, but it may also contain any general discussion of the purpose or behavior of the module's functions, and discussion of changes made to the module.
- DESCRIPTION - If not included in the SYNOPSIS, a general description of the module's purpose should appear here.
- FUNCTIONS - Descriptions of each function in the module, beginning with an example of how the function is called and continuing with a description of the behavior of the function.
- AUTHOR - This will generally be the "Koha Development Team," but may also be an individual developer who has made significant contributions to the module. Give a contact e-mail address.
Example:
Koha::Widget; use Modern::Perl; =head1 NAME Koha::Widget - A widget module for Koha =head1 SYNOPSIS use Koha::Widget; =head1 DESCRIPTION The Koha widget is an amazingly vague thing that I can't give a good description for. =head1 FUNCTIONS =head2 frobnicate my $foo = Koha::Widget::frobnicate( $bar ); This subroutine frobnicates the given variable into a widget. =cut sub frobnicate { ... } =head2 fluzzle my $foo = Koha::Widget::fluzzle( { param1 => $fizz, param2 => $buzz } ); Accepts two parameters and fluzzles the combination of the two. =cut sub fluzzle { ... }
Exports (PERL14)
Subroutines exported from modules in the C4 and Koha namespaces should be kept to an absolute minimum.
Good-Practices should be followed.
Ideally there should be no routines at all exported from Koha:: though this ideal may not be attainable.
Object-oriented code and the Koha namespace (PERL15)
Whenever it makes sense, code added to the Koha:: namespace should be object-oriented.
However, code that is naturally procedural should not be shoehorned into the OO style.
Modules in the Koha
namespace should not reference the C4 namespace, with the exception of C4::Context
.
Object Oriented Programming in Koha
Koha::Object(s) (PERL20)
Modules in the Koha namespace should be object oriented when possible, using Koha::Object(s) as a preferred base.
If a Koha::Object already exists, use it instead of other methods of table CRUD.
Tables can be turned into Objects trivially with Koha Objects
Koha objects can be extended with new methods. Koha::Object(s) is the preferred way to create OO modules in Koha at this time.
Class::Accessor
If your module is done database driven, Class::Accessor may be useful to quickly add setter and getter methods to your class.
Class::Accessor is a great way to provide access to member variables:
use base qw(Class::Accessor); __PACKAGE__->mk_accessors(qw( member1 member2 ));
Those two lines at the top of the package are sufficient to create read/write accessors to the member variables $self->{'member1'} and $self->{'member2'} that can be accessed as follows:
warn $object->member1;
$object->member2('newvalue');
A useful idiom new()
A useful idiom for the ->new() routine in object-oriented classes that do not need to process the arguments passed in as a hashref but merely need to save them for future processing:
sub new {
my ($class, $args) = @_;
$args = {} unless defined $args;
return bless ($args, $class);
}
An object with that initialization routine can be created with:
my $obj = Koha::Object->new({ 'member1' => 'value1', 'member2' => 'value2' });
Unit tests (PERL17)
Unit tests must be provided for *ALL* new routines added to the C4
and Koha
namespaces.
Statement coverage should be as close to 100% as possible. In order to check test coverage, you will need to install Devel::Cover and run the following commands:
perl -MDevel::Cover ${MYTEST} cover
This will generate a report in cover_db/coverage.html which you can review for coverage issues.
For more about unit tests and continuous integration, see Unit Tests and Continuous Integration.
User Interface
HTML
Form Markup
Toolbar Markup
JavaScript
String Translation
Form Validation
All form validation should use the jQuery Validation Plugin when possible. This is the standard method of validating form data in Koha and it is already included in Koha!
To enable form validation for a given form, start by adding the class validated to your form.
<form action="[% script_name %]" method="post" class="validated">
next, you can specify that a patch be required with markup similar to this:
<input type="text" name="input_name" class="required" required="required" /> <span class="required">Required</span>
for more advanced validation, see the jQuery Validation Plugin documentation at the link above.
CSS
Updating OPAC CSS
Adding icons
Database
Submitting Patches with Updates
If a patch you submit requires adding data to the database, or a change to the database schema, the best practice is to create a file in the atomicupdates directory ( installer/data/mysql/atomicupdate/ ).
See Database_updates#How_to_write_a_database_update
Table Naming Conventions
- Each table should be plural ( e.g. patrons, not patron )
- Tables with multiple words should be snake case ( e.g. patron_eye_colors )
- Each table should have an arbitrary primary field ( no compound primary keys )
Table Column Conventions
- The primary key should be named 'id'
- All fields should be named in snake case ( e.g. patron_eye_colors )
- A timestamp that is set only when a row is created should be named 'created_on'
- A timestamp that is set whenever a row is updated should be named 'updated_on'
- It is preferable to have 'created_on' and 'updated_on' fields for each new table.
- Koha::Object(s) makes it easy to set these timestamps, since older version of mysql cannot do this.