diff --git a/crm/_gen.php b/crm/_gen.php
new file mode 100755
index 000000000..67d36e671
--- /dev/null
+++ b/crm/_gen.php
@@ -0,0 +1,71 @@
+#!/usr/bin/env php
+set_compile_location("/tmp", true);
+$tpl->add_default("newline", "\n");
+
+$api_files = glob("docs/src/spec/*.json");
+$apis = array_map(function($filename) {
+ return array_change_key_case_recursive(json_decode(file_get_contents($filename), true));
+}, $api_files);
+
+usort($apis, function($a, $b) {
+ return strcmp($a['interface'], $b['interface']);
+});
+
+foreach (array("structs", "handlers", "interfaces", "request", "") as $type) {
+ foreach ($apis as $api) {
+ if (!empty($api['struct'])) {
+ $name = ucfirst($api['interface']);
+ $filename = str_replace("..", ".", strtolower($name) . "." . $type . ".go");
+
+ $tpl->load("http_$type.tpl");
+ $tpl->assign("parsers", array(
+ "uint64" => "parseUInt64"
+ ));
+ $tpl->assign("package", $api['package']);
+ $tpl->assign("name", $name);
+ $tpl->assign("self", strtolower(substr($name, 0, 1)));
+ $tpl->assign("api", $api);
+ $tpl->assign("fields", $api['struct']);
+ $tpl->assign("calls", $api['apis']);
+ $contents = $tpl->get();
+
+ $save = true;
+ if ($type === "" && file_exists($filename)) {
+ $save = false;
+ }
+ if ($save) {
+ file_put_contents($filename, $contents);
+ }
+ }
+ }
+}
+
+foreach (array("routes") as $type) {
+ $name = ucfirst($api['interface']);
+ $filename = str_replace("..", ".", $type . ".go");
+
+ $tpl->load("http_$type.tpl");
+ $tpl->assign("package", reset($apis)['package']);
+ $tpl->assign("apis", $apis);
+ $contents = $tpl->get();
+
+ file_put_contents($filename, $contents);
+}
+
+passthru("go fmt");
\ No newline at end of file
diff --git a/crm/docs/README.md b/crm/docs/README.md
new file mode 100644
index 000000000..7fe802d6a
--- /dev/null
+++ b/crm/docs/README.md
@@ -0,0 +1,30 @@
+# Types
+
+Types are building blocks for module forms
+
+## List available types
+
+#### Method
+
+| URI | Protocol | Method | Authentication |
+| --- | -------- | ------ | -------------- |
+| `/types/list` | HTTP/S | GET | |
+
+#### Request parameters
+
+| Parameter | Type | Method | Description | Default | Required? |
+| --------- | ---- | ------ | ----------- | ------- | --------- |
+
+## Get type details
+
+#### Method
+
+| URI | Protocol | Method | Authentication |
+| --- | -------- | ------ | -------------- |
+| `/types/type/{id}` | HTTP/S | GET | |
+
+#### Request parameters
+
+| Parameter | Type | Method | Description | Default | Required? |
+| --------- | ---- | ------ | ----------- | ------- | --------- |
+| id | string | PATH | Type ID | N/A | YES |
\ No newline at end of file
diff --git a/crm/docs/README.php b/crm/docs/README.php
new file mode 100755
index 000000000..269d69adc
--- /dev/null
+++ b/crm/docs/README.php
@@ -0,0 +1,66 @@
+#!/usr/bin/php
+ $v) {
+ if ($v === "") {
+ if ($empty) {
+ unset($lines[$k]);
+ }
+ $empty = true;
+ continue;
+ }
+ $empty = false;
+ }
+ $contents = implode("\n", $lines);
+ $contents = str_replace("`\n|", "` |", $contents);
+ $contents = preg_replace("/^# /sm", "\n\n\n# ", $contents);
+ return trim($contents);
+}
+
+$spec = json_decode(file_get_contents("src/spec.json"), true);
+
+$apis = array();
+foreach ($spec as $api) {
+ $entrypoint = $api['entrypoint'];
+ $filename = "src/spec/" . $entrypoint . ".json";
+ $filename_md = "src/" . $entrypoint . "/index.md";
+ $api = json_decode(file_get_contents($filename), true);
+ $api = array_change_key_case_recursive($api);
+ if (file_exists($filename_md)) {
+ $api['description'] = file_get_contents($filename_md);
+ }
+ $calls = array();
+ foreach ($api['apis'] as $call) {
+ $name = $call['name'];
+ $filename_md = "src/" . $entrypoint . "/" . $name . ".md";
+ if (file_exists($filename_md)) {
+ $call['description'] = file_get_contents($filename_md);
+ }
+ $calls[$name] = $call;
+ }
+ $api['apis'] = $calls;
+ $apis[] = $api;
+}
+
+$tpl = new Monotek\MiniTPL\Template;
+$tpl->set_compile_location("/tmp", true);
+$tpl->set_paths("./");
+$tpl->load("README.tpl");
+$tpl->assign("apis", $apis);
+file_put_contents("README.md", cleanUp($tpl->get()));
\ No newline at end of file
diff --git a/crm/docs/README.tpl b/crm/docs/README.tpl
new file mode 100644
index 000000000..4960f1e1b
--- /dev/null
+++ b/crm/docs/README.tpl
@@ -0,0 +1,31 @@
+{foreach $apis as $api}
+# {api.title}
+
+{api.description}
+
+{foreach $api.apis as $name => $call}
+ ## {call.title}
+
+ {call.description}
+
+ #### Method
+
+ | URI | Protocol | Method | Authentication |
+ | --- | -------- | ------ | -------------- |
+ | `{api.path}{call.path}` | {if $api.protocol}{api.protocol}{else}HTTP/S{/if} | {call.method} | {eval echo implode(", ", $api.authentication)} |
+
+ #### Request parameters
+
+ | Parameter | Type | Method | Description | Default | Required? |
+ | --------- | ---- | ------ | ----------- | ------- | --------- |
+ {foreach $call.parameters as $method => $params}
+ {foreach $params as $param}
+ | {param.name} | {param.type} | {method|toupper} | {param.title} | {if empty($param.default)}N/A{else}{param.default}{/if}
+{if $param.values}
Values:
{foreach $param.values as $value}- `{value}`
{/foreach}{/if} | {if $param.required}YES{else}NO{/if} |
+ {/foreach}
+ {/foreach}
+
+
+{/foreach}
+
+{/foreach}
\ No newline at end of file
diff --git a/crm/docs/composer.json b/crm/docs/composer.json
new file mode 100644
index 000000000..eae1c2b80
--- /dev/null
+++ b/crm/docs/composer.json
@@ -0,0 +1,5 @@
+{
+ "require": {
+ "monotek/minitpl": "^1.1"
+ }
+}
diff --git a/crm/docs/composer.lock b/crm/docs/composer.lock
new file mode 100644
index 000000000..2722ba0bc
--- /dev/null
+++ b/crm/docs/composer.lock
@@ -0,0 +1,67 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+ "This file is @generated automatically"
+ ],
+ "hash": "27ca9291b11653ca55b2eb53454a2d60",
+ "content-hash": "789a4ed7b74ed2be14054128f82abcde",
+ "packages": [
+ {
+ "name": "monotek/minitpl",
+ "version": "v1.1.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/titpetric/minitpl.git",
+ "reference": "613a596b3dee3c121283fad3f84d06d83ecb5125"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/titpetric/minitpl/zipball/613a596b3dee3c121283fad3f84d06d83ecb5125",
+ "reference": "613a596b3dee3c121283fad3f84d06d83ecb5125",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-0": {
+ "Monotek\\MiniTPL": "code"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "CC BY-SA 3.0"
+ ],
+ "authors": [
+ {
+ "name": "Tit Petric",
+ "email": "tit.petric@monotek.net",
+ "homepage": "http://titpetric.github.io",
+ "role": "developer"
+ }
+ ],
+ "description": "Miniature fully featured PHP template engine",
+ "homepage": "https://github.com/titpetric/minitpl",
+ "keywords": [
+ "minitpl",
+ "monotek",
+ "php",
+ "small",
+ "smarty",
+ "template",
+ "tiny"
+ ],
+ "time": "2016-02-07 13:23:38"
+ }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": []
+}
diff --git a/crm/docs/src/spec.json b/crm/docs/src/spec.json
new file mode 100644
index 000000000..1e400c902
--- /dev/null
+++ b/crm/docs/src/spec.json
@@ -0,0 +1,31 @@
+[
+ {
+ "title": "Types",
+ "description": "Types are building blocks for module forms",
+ "package": "crm",
+ "entrypoint": "types",
+ "path": "types",
+ "authentication": [],
+ "struct": [
+ { "name": "ID", "type": "string" }
+ ],
+ "apis": [
+ {
+ "name": "list",
+ "method": "GET",
+ "title": "List available types"
+ },
+ {
+ "name": "type",
+ "path": "/type/{id}",
+ "method": "GET",
+ "title": "Get type details",
+ "parameters": {
+ "path": [
+ { "type": "string", "name": "id", "required": true, "title": "Type ID" }
+ ]
+ }
+ }
+ ]
+ }
+]
\ No newline at end of file
diff --git a/crm/docs/src/spec/types.json b/crm/docs/src/spec/types.json
new file mode 100644
index 000000000..e224c6939
--- /dev/null
+++ b/crm/docs/src/spec/types.json
@@ -0,0 +1,40 @@
+{
+ "Title": "Types",
+ "Description": "Types are building blocks for module forms",
+ "Package": "crm",
+ "Interface": "Types",
+ "Struct": [
+ {
+ "name": "ID",
+ "type": "string"
+ }
+ ],
+ "Protocol": "",
+ "Authentication": [],
+ "Path": "/types",
+ "APIs": [
+ {
+ "Name": "list",
+ "Method": "GET",
+ "Title": "List available types",
+ "Path": "/list",
+ "Parameters": null
+ },
+ {
+ "Name": "type",
+ "Method": "GET",
+ "Title": "Get type details",
+ "Path": "/type/{id}",
+ "Parameters": {
+ "path": [
+ {
+ "name": "id",
+ "required": true,
+ "title": "Type ID",
+ "type": "string"
+ }
+ ]
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/crm/docs/vendor/autoload.php b/crm/docs/vendor/autoload.php
new file mode 100644
index 000000000..7787228a5
--- /dev/null
+++ b/crm/docs/vendor/autoload.php
@@ -0,0 +1,7 @@
+
+ * Jordi Boggiano
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Autoload;
+
+/**
+ * ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
+ *
+ * $loader = new \Composer\Autoload\ClassLoader();
+ *
+ * // register classes with namespaces
+ * $loader->add('Symfony\Component', __DIR__.'/component');
+ * $loader->add('Symfony', __DIR__.'/framework');
+ *
+ * // activate the autoloader
+ * $loader->register();
+ *
+ * // to enable searching the include path (eg. for PEAR packages)
+ * $loader->setUseIncludePath(true);
+ *
+ * In this example, if you try to use a class in the Symfony\Component
+ * namespace or one of its children (Symfony\Component\Console for instance),
+ * the autoloader will first look for the class under the component/
+ * directory, and it will then fallback to the framework/ directory if not
+ * found before giving up.
+ *
+ * This class is loosely based on the Symfony UniversalClassLoader.
+ *
+ * @author Fabien Potencier
+ * @author Jordi Boggiano
+ * @see http://www.php-fig.org/psr/psr-0/
+ * @see http://www.php-fig.org/psr/psr-4/
+ */
+class ClassLoader
+{
+ // PSR-4
+ private $prefixLengthsPsr4 = array();
+ private $prefixDirsPsr4 = array();
+ private $fallbackDirsPsr4 = array();
+
+ // PSR-0
+ private $prefixesPsr0 = array();
+ private $fallbackDirsPsr0 = array();
+
+ private $useIncludePath = false;
+ private $classMap = array();
+
+ private $classMapAuthoritative = false;
+
+ public function getPrefixes()
+ {
+ if (!empty($this->prefixesPsr0)) {
+ return call_user_func_array('array_merge', $this->prefixesPsr0);
+ }
+
+ return array();
+ }
+
+ public function getPrefixesPsr4()
+ {
+ return $this->prefixDirsPsr4;
+ }
+
+ public function getFallbackDirs()
+ {
+ return $this->fallbackDirsPsr0;
+ }
+
+ public function getFallbackDirsPsr4()
+ {
+ return $this->fallbackDirsPsr4;
+ }
+
+ public function getClassMap()
+ {
+ return $this->classMap;
+ }
+
+ /**
+ * @param array $classMap Class to filename map
+ */
+ public function addClassMap(array $classMap)
+ {
+ if ($this->classMap) {
+ $this->classMap = array_merge($this->classMap, $classMap);
+ } else {
+ $this->classMap = $classMap;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix, either
+ * appending or prepending to the ones previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param array|string $paths The PSR-0 root directories
+ * @param bool $prepend Whether to prepend the directories
+ */
+ public function add($prefix, $paths, $prepend = false)
+ {
+ if (!$prefix) {
+ if ($prepend) {
+ $this->fallbackDirsPsr0 = array_merge(
+ (array) $paths,
+ $this->fallbackDirsPsr0
+ );
+ } else {
+ $this->fallbackDirsPsr0 = array_merge(
+ $this->fallbackDirsPsr0,
+ (array) $paths
+ );
+ }
+
+ return;
+ }
+
+ $first = $prefix[0];
+ if (!isset($this->prefixesPsr0[$first][$prefix])) {
+ $this->prefixesPsr0[$first][$prefix] = (array) $paths;
+
+ return;
+ }
+ if ($prepend) {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ (array) $paths,
+ $this->prefixesPsr0[$first][$prefix]
+ );
+ } else {
+ $this->prefixesPsr0[$first][$prefix] = array_merge(
+ $this->prefixesPsr0[$first][$prefix],
+ (array) $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace, either
+ * appending or prepending to the ones previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param array|string $paths The PSR-4 base directories
+ * @param bool $prepend Whether to prepend the directories
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function addPsr4($prefix, $paths, $prepend = false)
+ {
+ if (!$prefix) {
+ // Register directories for the root namespace.
+ if ($prepend) {
+ $this->fallbackDirsPsr4 = array_merge(
+ (array) $paths,
+ $this->fallbackDirsPsr4
+ );
+ } else {
+ $this->fallbackDirsPsr4 = array_merge(
+ $this->fallbackDirsPsr4,
+ (array) $paths
+ );
+ }
+ } elseif (!isset($this->prefixDirsPsr4[$prefix])) {
+ // Register directories for a new namespace.
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ } elseif ($prepend) {
+ // Prepend directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ (array) $paths,
+ $this->prefixDirsPsr4[$prefix]
+ );
+ } else {
+ // Append directories for an already registered namespace.
+ $this->prefixDirsPsr4[$prefix] = array_merge(
+ $this->prefixDirsPsr4[$prefix],
+ (array) $paths
+ );
+ }
+ }
+
+ /**
+ * Registers a set of PSR-0 directories for a given prefix,
+ * replacing any others previously set for this prefix.
+ *
+ * @param string $prefix The prefix
+ * @param array|string $paths The PSR-0 base directories
+ */
+ public function set($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr0 = (array) $paths;
+ } else {
+ $this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Registers a set of PSR-4 directories for a given namespace,
+ * replacing any others previously set for this namespace.
+ *
+ * @param string $prefix The prefix/namespace, with trailing '\\'
+ * @param array|string $paths The PSR-4 base directories
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function setPsr4($prefix, $paths)
+ {
+ if (!$prefix) {
+ $this->fallbackDirsPsr4 = (array) $paths;
+ } else {
+ $length = strlen($prefix);
+ if ('\\' !== $prefix[$length - 1]) {
+ throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
+ }
+ $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
+ $this->prefixDirsPsr4[$prefix] = (array) $paths;
+ }
+ }
+
+ /**
+ * Turns on searching the include path for class files.
+ *
+ * @param bool $useIncludePath
+ */
+ public function setUseIncludePath($useIncludePath)
+ {
+ $this->useIncludePath = $useIncludePath;
+ }
+
+ /**
+ * Can be used to check if the autoloader uses the include path to check
+ * for classes.
+ *
+ * @return bool
+ */
+ public function getUseIncludePath()
+ {
+ return $this->useIncludePath;
+ }
+
+ /**
+ * Turns off searching the prefix and fallback directories for classes
+ * that have not been registered with the class map.
+ *
+ * @param bool $classMapAuthoritative
+ */
+ public function setClassMapAuthoritative($classMapAuthoritative)
+ {
+ $this->classMapAuthoritative = $classMapAuthoritative;
+ }
+
+ /**
+ * Should class lookup fail if not found in the current class map?
+ *
+ * @return bool
+ */
+ public function isClassMapAuthoritative()
+ {
+ return $this->classMapAuthoritative;
+ }
+
+ /**
+ * Registers this instance as an autoloader.
+ *
+ * @param bool $prepend Whether to prepend the autoloader or not
+ */
+ public function register($prepend = false)
+ {
+ spl_autoload_register(array($this, 'loadClass'), true, $prepend);
+ }
+
+ /**
+ * Unregisters this instance as an autoloader.
+ */
+ public function unregister()
+ {
+ spl_autoload_unregister(array($this, 'loadClass'));
+ }
+
+ /**
+ * Loads the given class or interface.
+ *
+ * @param string $class The name of the class
+ * @return bool|null True if loaded, null otherwise
+ */
+ public function loadClass($class)
+ {
+ if ($file = $this->findFile($class)) {
+ includeFile($file);
+
+ return true;
+ }
+ }
+
+ /**
+ * Finds the path to the file where the class is defined.
+ *
+ * @param string $class The name of the class
+ *
+ * @return string|false The path if found, false otherwise
+ */
+ public function findFile($class)
+ {
+ // work around for PHP 5.3.0 - 5.3.2 https://bugs.php.net/50731
+ if ('\\' == $class[0]) {
+ $class = substr($class, 1);
+ }
+
+ // class map lookup
+ if (isset($this->classMap[$class])) {
+ return $this->classMap[$class];
+ }
+ if ($this->classMapAuthoritative) {
+ return false;
+ }
+
+ $file = $this->findFileWithExtension($class, '.php');
+
+ // Search for Hack files if we are running on HHVM
+ if ($file === null && defined('HHVM_VERSION')) {
+ $file = $this->findFileWithExtension($class, '.hh');
+ }
+
+ if ($file === null) {
+ // Remember that this class does not exist.
+ return $this->classMap[$class] = false;
+ }
+
+ return $file;
+ }
+
+ private function findFileWithExtension($class, $ext)
+ {
+ // PSR-4 lookup
+ $logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
+
+ $first = $class[0];
+ if (isset($this->prefixLengthsPsr4[$first])) {
+ foreach ($this->prefixLengthsPsr4[$first] as $prefix => $length) {
+ if (0 === strpos($class, $prefix)) {
+ foreach ($this->prefixDirsPsr4[$prefix] as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $length))) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-4 fallback dirs
+ foreach ($this->fallbackDirsPsr4 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 lookup
+ if (false !== $pos = strrpos($class, '\\')) {
+ // namespaced class name
+ $logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
+ . strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
+ } else {
+ // PEAR-like class name
+ $logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
+ }
+
+ if (isset($this->prefixesPsr0[$first])) {
+ foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
+ if (0 === strpos($class, $prefix)) {
+ foreach ($dirs as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+ }
+ }
+ }
+
+ // PSR-0 fallback dirs
+ foreach ($this->fallbackDirsPsr0 as $dir) {
+ if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
+ return $file;
+ }
+ }
+
+ // PSR-0 include paths.
+ if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
+ return $file;
+ }
+ }
+}
+
+/**
+ * Scope isolated include.
+ *
+ * Prevents access to $this/self from included files.
+ */
+function includeFile($file)
+{
+ include $file;
+}
diff --git a/crm/docs/vendor/composer/LICENSE b/crm/docs/vendor/composer/LICENSE
new file mode 100644
index 000000000..1a2812488
--- /dev/null
+++ b/crm/docs/vendor/composer/LICENSE
@@ -0,0 +1,21 @@
+
+Copyright (c) 2016 Nils Adermann, Jordi Boggiano
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
diff --git a/crm/docs/vendor/composer/autoload_classmap.php b/crm/docs/vendor/composer/autoload_classmap.php
new file mode 100644
index 000000000..7a91153b0
--- /dev/null
+++ b/crm/docs/vendor/composer/autoload_classmap.php
@@ -0,0 +1,9 @@
+ array($vendorDir . '/monotek/minitpl/code'),
+);
diff --git a/crm/docs/vendor/composer/autoload_psr4.php b/crm/docs/vendor/composer/autoload_psr4.php
new file mode 100644
index 000000000..b265c64a2
--- /dev/null
+++ b/crm/docs/vendor/composer/autoload_psr4.php
@@ -0,0 +1,9 @@
+= 50600 && !defined('HHVM_VERSION');
+ if ($useStaticLoader) {
+ require_once __DIR__ . '/autoload_static.php';
+
+ call_user_func(\Composer\Autoload\ComposerStaticInita6e366c180070d1defcfe90f77724cd9::getInitializer($loader));
+ } else {
+ $map = require __DIR__ . '/autoload_namespaces.php';
+ foreach ($map as $namespace => $path) {
+ $loader->set($namespace, $path);
+ }
+
+ $map = require __DIR__ . '/autoload_psr4.php';
+ foreach ($map as $namespace => $path) {
+ $loader->setPsr4($namespace, $path);
+ }
+
+ $classMap = require __DIR__ . '/autoload_classmap.php';
+ if ($classMap) {
+ $loader->addClassMap($classMap);
+ }
+ }
+
+ $loader->register(true);
+
+ return $loader;
+ }
+}
diff --git a/crm/docs/vendor/composer/autoload_static.php b/crm/docs/vendor/composer/autoload_static.php
new file mode 100644
index 000000000..55982c4a6
--- /dev/null
+++ b/crm/docs/vendor/composer/autoload_static.php
@@ -0,0 +1,26 @@
+
+ array (
+ 'Monotek\\MiniTPL' =>
+ array (
+ 0 => __DIR__ . '/..' . '/monotek/minitpl/code',
+ ),
+ ),
+ );
+
+ public static function getInitializer(ClassLoader $loader)
+ {
+ return \Closure::bind(function () use ($loader) {
+ $loader->prefixesPsr0 = ComposerStaticInita6e366c180070d1defcfe90f77724cd9::$prefixesPsr0;
+
+ }, null, ClassLoader::class);
+ }
+}
diff --git a/crm/docs/vendor/composer/installed.json b/crm/docs/vendor/composer/installed.json
new file mode 100644
index 000000000..704eaf5a2
--- /dev/null
+++ b/crm/docs/vendor/composer/installed.json
@@ -0,0 +1,52 @@
+[
+ {
+ "name": "monotek/minitpl",
+ "version": "v1.1.4",
+ "version_normalized": "1.1.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/titpetric/minitpl.git",
+ "reference": "613a596b3dee3c121283fad3f84d06d83ecb5125"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/titpetric/minitpl/zipball/613a596b3dee3c121283fad3f84d06d83ecb5125",
+ "reference": "613a596b3dee3c121283fad3f84d06d83ecb5125",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "time": "2016-02-07 13:23:38",
+ "type": "library",
+ "installation-source": "dist",
+ "autoload": {
+ "psr-0": {
+ "Monotek\\MiniTPL": "code"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "CC BY-SA 3.0"
+ ],
+ "authors": [
+ {
+ "name": "Tit Petric",
+ "email": "tit.petric@monotek.net",
+ "homepage": "http://titpetric.github.io",
+ "role": "developer"
+ }
+ ],
+ "description": "Miniature fully featured PHP template engine",
+ "homepage": "https://github.com/titpetric/minitpl",
+ "keywords": [
+ "minitpl",
+ "monotek",
+ "php",
+ "small",
+ "smarty",
+ "template",
+ "tiny"
+ ]
+ }
+]
diff --git a/crm/docs/vendor/monotek/minitpl/.gitignore b/crm/docs/vendor/monotek/minitpl/.gitignore
new file mode 100644
index 000000000..2dd5d1617
--- /dev/null
+++ b/crm/docs/vendor/monotek/minitpl/.gitignore
@@ -0,0 +1,2 @@
+test/compile
+vendor
diff --git a/crm/docs/vendor/monotek/minitpl/.gitmodules b/crm/docs/vendor/monotek/minitpl/.gitmodules
new file mode 100644
index 000000000..166a06f5a
--- /dev/null
+++ b/crm/docs/vendor/monotek/minitpl/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "git_hooks"]
+ path = git_hooks
+ url = git@github.com:titpetric/git_hooks.git
diff --git a/crm/docs/vendor/monotek/minitpl/README.markdown b/crm/docs/vendor/monotek/minitpl/README.markdown
new file mode 100644
index 000000000..97645c8b6
--- /dev/null
+++ b/crm/docs/vendor/monotek/minitpl/README.markdown
@@ -0,0 +1,37 @@
+# MiniTPL
+
+The goal of the MiniTPL template engine is to provide a miniature
+framework which allows you to rapidly create and consume
+Smarty-like templates without adding the overhead of Smarty to
+your choice of a PHP framework.
+
+In benchmarks the speed of Mini TPL is very close to PHP itself.
+All that is usually needed for Mini TPL is a 3KB PHP code overhead.
+So it beats Smarty, and usual PHP vsprintf and str_replace functionality.
+
+With a total size of about 13KB and the functionality contained, this is
+one of the smallest full featured template engines for PHP to date.
+
+MiniTPL is available on [packagist as monotek/minitpl](https://packagist.org/packages/monotek/minitpl).
+
+To start using MiniTPL in your project with [composer](http://getcomposer.org/), create a composer.json file:
+```
+{
+ "require": {
+ "monotek/minitpl": ">=1.0"
+ }
+}
+```
+
+And run `composer install`. You can start using MiniTPL right away
+
+```
+load("test.tpl");
+$tpl->render();
+```
\ No newline at end of file
diff --git a/crm/docs/vendor/monotek/minitpl/code/Monotek/MiniTPL/Compiler.php b/crm/docs/vendor/monotek/minitpl/code/Monotek/MiniTPL/Compiler.php
new file mode 100644
index 000000000..c87c67139
--- /dev/null
+++ b/crm/docs/vendor/monotek/minitpl/code/Monotek/MiniTPL/Compiler.php
@@ -0,0 +1,345 @@
+_tag_php_open = "<"."?php";
+ $this->_tag_php_close = "?".">\n";
+ $this->_global_variables = array();
+ $this->_literals = array();
+ }
+
+ protected function load_contents($filename)
+ {
+ if (($contents = file_get_contents($filename)) !== false) {
+ if (substr($contents, 0, 3) == "\xEF\xBB\xBF") {
+ return substr($contents, 3);
+ }
+ }
+ return $contents;
+ }
+
+ /** Compile template file into php code */
+ function compile($filename, $output_filename, $find_path, $nocache)
+ {
+ $contents = $this->load_contents($filename);
+ $r = 0;
+ if ($contents!==false && $contents!=="") {
+ while (preg_match_all("/\{include\ (.*?)\}/s", $contents, $matches)) {
+ $matches = array_unique($matches[1]);
+ foreach ($matches as $file) {
+ $cn = "";
+ if (($fn = call_user_func($find_path, $file))!==false) {
+ $cn = $this->load_contents($fn.$file);
+ }
+ $contents = str_replace("{include ".$file."}", $cn, $contents);
+ }
+ }
+ while (preg_match_all("/\{load\ (.*?)\}/s", $contents, $matches)) {
+ $matches = array_unique($matches[1]);
+ foreach ($matches as $file) {
+ $file_var = (substr($file,0,1) == '$') ? $this->_split_exp($file) : '"'.$file.'"';
+ $cn = $this->_code('$this->push();$this->load('.$file_var.');$this->assign($_v);$this->render();$this->pop();');
+ $contents = str_replace("{load ".$file."}", $cn, $contents);
+ }
+ }
+ $nocache = $nocache ? $this->_code("@unlink(__FILE__);") : "";
+ $contents = str_replace("{*nocache*}",$nocache,$contents);
+ $contents = $this->_strip_comments($contents);
+ $contents = $this->_parse_constants($contents);
+ $contents = $this->_parse_functions($contents, $filename);
+ $contents = $this->_parse_expressions($contents);
+ $contents = $this->_parse_variables($contents);
+ if (!empty($this->_global_variables)) {
+ $globals = array_unique($this->_global_variables);
+ $contents = $this->_code('global '.implode(", ",$globals).';').$contents;
+ }
+ $contents = $this->_template_cleanup($contents);
+ $this->_r_mkdir(dirname($output_filename));
+ if ($f = @fopen($output_filename,"w")) {
+ fwrite($f, $contents);
+ fclose($f);
+ $r = 1;
+ }
+ }
+ return $r;
+ }
+
+ function _r_mkdir($dir)
+ {
+ if (file_exists($dir)) return;
+ if (!file_exists(dirname($dir))) $this->_r_mkdir(dirname($dir));
+ @mkdir($dir);
+ }
+
+ /** Insert system configuration, clean up code */
+ function _template_cleanup($contents)
+ {
+ // set up variables
+ $contents = $this->_code('$_v=&$this->vars;') . $contents;
+ // strip unnecessary php tags
+ $contents = str_replace($this->_tag_php_close.$this->_tag_php_open, "", $contents);
+ // strip new line whitespace between php code
+ $contents = str_replace($this->_tag_php_close."\n".$this->_tag_php_open.' ', "", $contents);
+ $contents = str_replace("echo ;", "", $contents);
+ foreach ($this->_literals as $key=>$value) {
+ $contents = str_replace("[[".$key."]]", $value, $contents);
+ }
+ return $contents;
+ }
+
+ /** Strip template style comments */
+ function _strip_comments($contents)
+ {
+ $contents = preg_replace("/\{\*.+\*\}/sU", "", $contents);
+ return $contents;
+ }
+
+ /** Replace constant definitions */
+ function _parse_constants($contents)
+ {
+ $matches = array();
+ if (preg_match_all("/\{(\_[a-zA-Z0-9\_]+)\}/", $contents, $matches)) {
+ $matches = array_unique($matches[1]);
+ foreach ($matches as $m) {
+ $contents = str_replace("{".$m."}", $this->_code("echo ".$m.";"), $contents);
+ }
+ }
+ return $contents;
+ }
+
+ /** Search and replace for function blocks and inline definitions */
+ function _parse_functions($contents, $filename)
+ {
+ $inlines = $blocks = array();
+
+ if (preg_match_all("/\{(block|inline)\ ([a-zA-Z0-9\_\-]+)\}(.*?)\{\/\\1\}/s", $contents, $matches)) {
+ foreach ($matches[0] as $k=>$ma) {
+ $m = array("content"=>trim($matches[3][$k]), "src"=>$ma);
+ if ($matches[1][$k]=="block") {
+ $blocks[$matches[2][$k]] = $m;
+ } else {
+ $inlines[$matches[2][$k]] = $m;
+ }
+ }
+ }
+
+ if (preg_match_all("/\
+
+Or this:
+
+
+
+
+But this works:
+
+
\ No newline at end of file
diff --git a/crm/docs/vendor/monotek/minitpl/test/compiled/11_expressions.tpl b/crm/docs/vendor/monotek/minitpl/test/compiled/11_expressions.tpl
new file mode 100644
index 000000000..42cbdf3a7
--- /dev/null
+++ b/crm/docs/vendor/monotek/minitpl/test/compiled/11_expressions.tpl
@@ -0,0 +1,15 @@
+vars; for($_v['i']=99; $_v['i']>=1; $_v['i']--){echo $_v['i'];?>
+ bottles of beer on the wall,
+ bottles of beer. Take one down and pass it around,
+ bottles of beer on the wall.
+ 10){?>
+
+ We have some beer left.
+ 3){?>
+
+ Our beer is going to run out.
+
+
+ Critical! Only
+ beer left.
+
\ No newline at end of file
diff --git a/crm/docs/vendor/monotek/minitpl/test/compiled/12_global_objects.tpl b/crm/docs/vendor/monotek/minitpl/test/compiled/12_global_objects.tpl
new file mode 100644
index 000000000..fc8689978
--- /dev/null
+++ b/crm/docs/vendor/monotek/minitpl/test/compiled/12_global_objects.tpl
@@ -0,0 +1,9 @@
+vars; global $tpl;?>
+There is a distinct difference between
+calling `$tpl->get` or `$tplx->get`.
+
+When `$tpl` is a global variable, the template engine detects this.
+
+_paths;?>
+ _paths;?>
+;
\ No newline at end of file
diff --git a/crm/docs/vendor/monotek/minitpl/test/compiled/13_foreach_array.tpl b/crm/docs/vendor/monotek/minitpl/test/compiled/13_foreach_array.tpl
new file mode 100644
index 000000000..c9b8a2412
--- /dev/null
+++ b/crm/docs/vendor/monotek/minitpl/test/compiled/13_foreach_array.tpl
@@ -0,0 +1,7 @@
+vars;?>
+We check if arrays are empty to suppress warnings/notices.
+
+
+HTTP
+HTTP
+HTTP
diff --git a/crm/docs/vendor/monotek/minitpl/test/templates/01_variable.tpl b/crm/docs/vendor/monotek/minitpl/test/templates/01_variable.tpl
new file mode 100644
index 000000000..c8936b7b9
--- /dev/null
+++ b/crm/docs/vendor/monotek/minitpl/test/templates/01_variable.tpl
@@ -0,0 +1,10 @@
+#### 1.1. Variables
+
+Using variables from templates is easy. Variables are enclosed
+in curly braces like so: `{variable}`. Since variables are parsed
+on the last stage, prefixing variables with the dollar sign is
+optional. So, `{$variable}` is the same as `{variable}`.
+
+~~~~~~~~~~~~~
+{variable} is the same as {$variable}
+~~~~~~~~~~~~~
diff --git a/crm/docs/vendor/monotek/minitpl/test/templates/02_arrays.tpl b/crm/docs/vendor/monotek/minitpl/test/templates/02_arrays.tpl
new file mode 100644
index 000000000..e40328bb3
--- /dev/null
+++ b/crm/docs/vendor/monotek/minitpl/test/templates/02_arrays.tpl
@@ -0,0 +1,13 @@
+#### 1.2. Arrays
+
+There is a shorthand syntax for using arrays inside a template.
+The array index separator is a dot (`.`). Consecutive dots can be
+used for traversing into array depth like `{$array.items.0}`, or
+even variables starting with `$`, like `{$sections.$news_section.title}`.
+PHP syntax for arrays is also supported.
+
+~~~~~~~~~~~~
+{$array.items} is the same as {$array['items']}
+{$array.items.0} is the same as {$array['items'][0]}
+{$array.$items.0} is the same as {$array[$items][0]}
+~~~~~~~~~~~~
diff --git a/crm/docs/vendor/monotek/minitpl/test/templates/03_modifiers.tpl b/crm/docs/vendor/monotek/minitpl/test/templates/03_modifiers.tpl
new file mode 100644
index 000000000..1674b2bce
--- /dev/null
+++ b/crm/docs/vendor/monotek/minitpl/test/templates/03_modifiers.tpl
@@ -0,0 +1,43 @@
+#### 1.3. Modifiers
+
+Modifiers are PHP functions, which take one arbitrary value
+(usually, string), and return a string, which gets shown.
+Some functions in PHP can be used as modifiers
+(strtoupper, ucfirst, str_rot13, strrev, count, ...).
+
+~~~~~~~~~~~~~~~~~~~~~~~~
+/** Custom modifier example */
+
+function add_it_up($array)
+{
+ $size = 0;
+ foreach ($array as $value) {
+ $size += $value['size'];
+ }
+ return $size;
+}
+~~~~~~~~~~~~~~~~~~~~~~~~
+
+To use the above function in a template, just do `{$variable|add_it_up}`,
+which will loop trough all `$variable` items and add up the value of
+the size element and return the total sum of all sizes.
+
+~~~~~~~~~~~~~~
+{variable|escape}
+{variable|toupper}
+{variable|add_id_up}
+~~~~~~~~~~~~~~
+
+The above code gets compiled to:
+
+~~~~~~~~~~~~~~
+vars;
+ echo htmlspecialchars($_v['variable'], ENT_QUOTES);
+ echo strtoupper($_v['variable']);
+ echo add_id_up($_v['variable']);
+?>
+~~~~~~~~~~~~~~
+
+A common use for modifiers is outputting data for javascript,
+using the php function `json_encode`.
diff --git a/crm/docs/vendor/monotek/minitpl/test/templates/04_objects.tpl b/crm/docs/vendor/monotek/minitpl/test/templates/04_objects.tpl
new file mode 100644
index 000000000..859148fe0
--- /dev/null
+++ b/crm/docs/vendor/monotek/minitpl/test/templates/04_objects.tpl
@@ -0,0 +1,53 @@
+#### 1.4. Objects
+
+You can also call object methods as modifiers, from global or
+local objects. The compiler will determine at run-time, if
+you have a global object by the name, and modify the code
+accordingly.
+
+~~~~~~~~~~~
+{$variable|$memcache->get}
+~~~~~~~~~~~
+
+If the global object doesn't exist, it assumes a local object.
+
+~~~~~~~~~~~
+vars;
+ echo $_v['memcache']->get($_v['variable']);
+?>
+~~~~~~~~~~~
+
+However, if a global object by the name $memcache exists at
+compile time:
+
+~~~~~~~~~~
+vars;
+ global $memcache;
+ echo $memcache->get($_v['variable']);
+?>
+~~~~~~~~~~
+
+You can also use variables from objects, in the same way.
+
+~~~~~~~~~~~~
+{$memcache->variable}
+~~~~~~~~~~~~
+
+Compiles to one of theese:
+
+~~~~~~~~~~
+vars;
+ echo $_v['memcache']->variable;
+?>
+~~~~~~~~~~
+
+~~~~~~~~~~
+vars;
+ global $memcache;
+ echo $memcache->variable;
+?>
+~~~~~~~~~~
diff --git a/crm/docs/vendor/monotek/minitpl/test/templates/05_constants.tpl b/crm/docs/vendor/monotek/minitpl/test/templates/05_constants.tpl
new file mode 100644
index 000000000..54fc8efcd
--- /dev/null
+++ b/crm/docs/vendor/monotek/minitpl/test/templates/05_constants.tpl
@@ -0,0 +1,13 @@
+#### 1.5. Constants
+
+Value starting with `_` is assumed to be a constant. The template
+system will output the value of the constant if defined,
+or just the name of the constant. Item `{_MY_CONSTANT}` will be
+used as a constant, because of those rules, however `{$_my_variable}`
+wouldnt be, since it starts with the variable identifier `$`.
+
+~~~~~~~~~~~
+{_MY_CONSTANT}
+{_this_is_also_a_constant}
+{$_my_variable}
+~~~~~~~~~~~
\ No newline at end of file
diff --git a/crm/docs/vendor/monotek/minitpl/test/templates/06_includes.tpl b/crm/docs/vendor/monotek/minitpl/test/templates/06_includes.tpl
new file mode 100644
index 000000000..d7d4a4455
--- /dev/null
+++ b/crm/docs/vendor/monotek/minitpl/test/templates/06_includes.tpl
@@ -0,0 +1,3 @@
+#### 1.6. Includes
+
+{include 05_constants.tpl}
diff --git a/crm/docs/vendor/monotek/minitpl/test/templates/07_eval.tpl b/crm/docs/vendor/monotek/minitpl/test/templates/07_eval.tpl
new file mode 100644
index 000000000..33f18dcdd
--- /dev/null
+++ b/crm/docs/vendor/monotek/minitpl/test/templates/07_eval.tpl
@@ -0,0 +1,12 @@
+{eval $key="nu"}
+
+{inline item}
+{if $i>1}{i}nd{else}first{/if}
+{/inline}
+
+{eval $i=1}
+{foreach $items as $index => $item}
+ {eval $id = $key . $i}
+ {inline:item}
+ {eval $i++}
+{/foreach}
diff --git a/crm/docs/vendor/monotek/minitpl/test/templates/08_utf8_bom.tpl b/crm/docs/vendor/monotek/minitpl/test/templates/08_utf8_bom.tpl
new file mode 100644
index 000000000..5e3d03c5f
--- /dev/null
+++ b/crm/docs/vendor/monotek/minitpl/test/templates/08_utf8_bom.tpl
@@ -0,0 +1,5 @@
+This file contains utf8 BOM.
+
+It also uses the {ldelim}load{rdelim} construct:
+
+{load 07_eval.tpl}
\ No newline at end of file
diff --git a/crm/docs/vendor/monotek/minitpl/test/templates/09_blocks_and_includes.tpl b/crm/docs/vendor/monotek/minitpl/test/templates/09_blocks_and_includes.tpl
new file mode 100644
index 000000000..695cd6dbb
--- /dev/null
+++ b/crm/docs/vendor/monotek/minitpl/test/templates/09_blocks_and_includes.tpl
@@ -0,0 +1,19 @@
+{inline snippet1}
+This is like a copy-paste over your {eval echo __FILE__}, anywhere you use this construct.
+{/inline}
+
+Functions bro, functions?
+
+{eval $i=1}
+{block function_call}
+This is a created function. This is the {i++}st/nd/rd/th time calling this method.
+{if $i <= 5}
+{block:function_call}
+{/if}
+{/block}
+
+{inline:snippet1}
+
+{inline:snippet1}
+
+{block:function_call}
\ No newline at end of file
diff --git a/crm/docs/vendor/monotek/minitpl/test/templates/10_literal_blocks.tpl b/crm/docs/vendor/monotek/minitpl/test/templates/10_literal_blocks.tpl
new file mode 100644
index 000000000..afea61e85
--- /dev/null
+++ b/crm/docs/vendor/monotek/minitpl/test/templates/10_literal_blocks.tpl
@@ -0,0 +1,20 @@
+The following block is literal (no template constructs work inside the tags)
+
+
+
+Or this:
+
+
+
+
+But this works:
+
+
\ No newline at end of file
diff --git a/crm/docs/vendor/monotek/minitpl/test/templates/11_expressions.tpl b/crm/docs/vendor/monotek/minitpl/test/templates/11_expressions.tpl
new file mode 100644
index 000000000..57a46c46b
--- /dev/null
+++ b/crm/docs/vendor/monotek/minitpl/test/templates/11_expressions.tpl
@@ -0,0 +1,11 @@
+{for $i=99; $i>=1; $i--}
+{i} bottles of beer on the wall, {i} bottles of beer. Take one down and pass it around, {eval echo $i-1} bottles of beer on the wall.
+{/for}
+
+{if $i > 10}
+ We have some beer left.
+{elseif $i > 3}
+ Our beer is going to run out.
+{else}
+ Critical! Only {i} beer left.
+{/if}
\ No newline at end of file
diff --git a/crm/docs/vendor/monotek/minitpl/test/templates/12_global_objects.tpl b/crm/docs/vendor/monotek/minitpl/test/templates/12_global_objects.tpl
new file mode 100644
index 000000000..5baf961b2
--- /dev/null
+++ b/crm/docs/vendor/monotek/minitpl/test/templates/12_global_objects.tpl
@@ -0,0 +1,6 @@
+There is a distinct difference between
+calling `$tpl->get` or `$tplx->get`.
+
+When `$tpl` is a global variable, the template engine detects this.
+
+{$tpl->_paths} {$tplx->_paths};
\ No newline at end of file
diff --git a/crm/docs/vendor/monotek/minitpl/test/templates/13_foreach_array.tpl b/crm/docs/vendor/monotek/minitpl/test/templates/13_foreach_array.tpl
new file mode 100644
index 000000000..0859bae21
--- /dev/null
+++ b/crm/docs/vendor/monotek/minitpl/test/templates/13_foreach_array.tpl
@@ -0,0 +1,13 @@
+We check if arrays are empty to suppress warnings/notices.
+
+{foreach array("GET", "POST") as $method}
+HTTP {method}
+{/foreach}
+
+{foreach (array("GET", "POST") as $method)}
+HTTP {method}
+{/foreach}
+
+{foreach $methods as $method}
+HTTP {method}
+{/foreach}
diff --git a/crm/docs/vendor/monotek/minitpl/test/templates2/fail_to_compile.tpl b/crm/docs/vendor/monotek/minitpl/test/templates2/fail_to_compile.tpl
new file mode 100644
index 000000000..e69de29bb
diff --git a/crm/routes.go b/crm/routes.go
new file mode 100644
index 000000000..22fdfdca6
--- /dev/null
+++ b/crm/routes.go
@@ -0,0 +1,13 @@
+package crm
+
+import (
+ "github.com/go-chi/chi"
+)
+
+func MountRoutes(r chi.Router) {
+ types := TypesHandlers{}.new()
+ r.Route("/types", func(r chi.Router) {
+ r.Get("/list", types.List)
+ r.Get("/type/{id}", types.Type)
+ })
+}
diff --git a/crm/templates/http_.tpl b/crm/templates/http_.tpl
new file mode 100644
index 000000000..abc4b9ec1
--- /dev/null
+++ b/crm/templates/http_.tpl
@@ -0,0 +1,11 @@
+package {package}
+
+import (
+ "github.com/pkg/errors"
+)
+
+{foreach $calls as $call}
+func ({self} *{name}) {call.name|ucfirst}(r *{name|lcfirst}{call.name|ucfirst}Request) (interface{}, error) {
+ return nil, errors.New("Not implemented: {name}.{call.name}")
+}
+{/foreach}
diff --git a/crm/templates/http_handlers.tpl b/crm/templates/http_handlers.tpl
new file mode 100644
index 000000000..3eab30652
--- /dev/null
+++ b/crm/templates/http_handlers.tpl
@@ -0,0 +1,14 @@
+package {package}
+
+import (
+ "net/http"
+
+ "github.com/titpetric/factory/resputil"
+)
+
+{foreach $calls as $call}
+func ({self}h *{name}Handlers) {call.name|ucfirst}(w http.ResponseWriter, r *http.Request) {
+ params := {name|lcfirst}{call.name|ucfirst}Request{}.new()
+ resputil.JSON(w, params.Fill(r), func() (interface{}, error) { return {self}h.{name}.{call.name|ucfirst}(params) })
+}
+{/foreach}
diff --git a/crm/templates/http_interfaces.tpl b/crm/templates/http_interfaces.tpl
new file mode 100644
index 000000000..8f2e2f213
--- /dev/null
+++ b/crm/templates/http_interfaces.tpl
@@ -0,0 +1,34 @@
+package {package}
+
+import (
+ "net/http"
+)
+
+// HTTP handlers are a superset of internal APIs
+type {name}Handlers struct {
+ *{name}
+}
+
+func ({name}Handlers) new() *{name}Handlers {
+ return &{name}Handlers{
+ {name}{}.new(),
+ }
+}
+
+// Internal API interface
+type {name}API interface {
+{foreach $calls as $call}
+ {call.name|ucfirst}(*{name|lcfirst}{call.name|ucfirst}Request) (interface{}, error)
+{/foreach}
+}
+
+// HTTP API interface
+type {name}HandlersAPI interface {
+{foreach $calls as $call}
+ {call.name|ucfirst}(http.ResponseWriter, *http.Request)
+{/foreach}
+}
+
+// Compile time check to see if we implement the interfaces
+var _ {name}HandlersAPI = &{name}Handlers{}
+var _ {name}API = &{name}{}
\ No newline at end of file
diff --git a/crm/templates/http_request.tpl b/crm/templates/http_request.tpl
new file mode 100644
index 000000000..92215dd16
--- /dev/null
+++ b/crm/templates/http_request.tpl
@@ -0,0 +1,43 @@
+package {package}
+
+import (
+ "net/http"
+)
+
+{foreach $calls as $call}
+// {name} {call.name} request parameters
+type {name|lcfirst}{call.name|ucfirst}Request struct {
+{foreach $call.parameters as $params}
+{foreach $params as $method => $param}
+ {param.name} {param.type}{newline}
+{/foreach}
+{/foreach}
+}
+
+func ({name|lcfirst}{call.name|ucfirst}Request) new() *{name|lcfirst}{call.name|ucfirst}Request {
+ return &{name|lcfirst}{call.name|ucfirst}Request{}
+}
+
+func ({self} *{name|lcfirst}{call.name|ucfirst}Request) Fill(r *http.Request) error {
+ get := map[string]string{}
+ post := map[string]string{}
+ urlQuery := r.URL.Query()
+ for name, param := range urlQuery {
+ get[name] = string(param[0])
+ }
+ postVars := r.Form
+ for name, param := range postVars {
+ post[name] = string(param[0])
+ }
+{foreach $call.parameters as $method => $params}
+{foreach $params as $param}
+{if substr($param.type, 0, 2) !== '[]'}
+ {self}.{param.name} = {if $param.type !== "string"}{$parsers[$param.type]}({method|strtolower}["{param.name}"]){else}{method|strtolower}["{param.name}"]{/if}{newline}
+{/if}
+{/foreach}
+{/foreach}
+ return nil
+}
+
+var _ RequestFiller = {name|lcfirst}{call.name|ucfirst}Request{}.new()
+{/foreach}
diff --git a/crm/templates/http_routes.tpl b/crm/templates/http_routes.tpl
new file mode 100644
index 000000000..61736bc86
--- /dev/null
+++ b/crm/templates/http_routes.tpl
@@ -0,0 +1,18 @@
+package {package}
+
+import (
+ "github.com/go-chi/chi"
+)
+
+func MountRoutes(r chi.Router) {
+{foreach $apis as $api}
+ {api.interface|strtolower} := {api.interface|ucfirst}Handlers{}.new()
+{/foreach}
+{foreach $apis as $api}
+ r.Route("{api.path}", func(r chi.Router) {
+{foreach $api.apis as $call}
+ r.{eval echo ucfirst(strtolower($call.method))}("{call.path}", {api.interface|strtolower}.{call.name|ucfirst})
+{/foreach}
+ })
+{/foreach}
+}
\ No newline at end of file
diff --git a/crm/templates/http_structs.tpl b/crm/templates/http_structs.tpl
new file mode 100644
index 000000000..a39d3b73d
--- /dev/null
+++ b/crm/templates/http_structs.tpl
@@ -0,0 +1,30 @@
+package {package}
+
+{if strpos($name, "Literal") !== false}
+ {foreach $fields as $field}
+ {field}{newline}
+ {/foreach}
+{else}
+ // {api.title}
+ type {name} struct {
+{foreach $fields as $field}
+ {field.name} {field.type}{if $field.tag} `{$field.tag}`{/if}{newline}
+{/foreach}
+ }
+
+func ({name}) new() *{name} {
+ return &{name}{}
+}
+
+{/if}
+
+{foreach $fields as $field}
+func ({self} *{name}) Get{field.name}() {field.type} {
+ return {self}.{field.name}
+}
+
+func ({self} *{name}) Set{field.name}(value {field.type}) *{name} {
+ {self}.{field.name} = value
+ return {self}
+}
+{/foreach}
diff --git a/crm/types.go b/crm/types.go
new file mode 100644
index 000000000..852aea3ae
--- /dev/null
+++ b/crm/types.go
@@ -0,0 +1,12 @@
+package crm
+
+import (
+ "github.com/pkg/errors"
+)
+
+func (t *Types) List(r *typesListRequest) (interface{}, error) {
+ return nil, errors.New("Not implemented: Types.list")
+}
+func (t *Types) Type(r *typesTypeRequest) (interface{}, error) {
+ return nil, errors.New("Not implemented: Types.type")
+}
diff --git a/crm/types.handlers.go b/crm/types.handlers.go
new file mode 100644
index 000000000..989eccc7c
--- /dev/null
+++ b/crm/types.handlers.go
@@ -0,0 +1,16 @@
+package crm
+
+import (
+ "net/http"
+
+ "github.com/titpetric/factory/resputil"
+)
+
+func (th *TypesHandlers) List(w http.ResponseWriter, r *http.Request) {
+ params := typesListRequest{}.new()
+ resputil.JSON(w, params.Fill(r), func() (interface{}, error) { return th.Types.List(params) })
+}
+func (th *TypesHandlers) Type(w http.ResponseWriter, r *http.Request) {
+ params := typesTypeRequest{}.new()
+ resputil.JSON(w, params.Fill(r), func() (interface{}, error) { return th.Types.Type(params) })
+}
diff --git a/crm/types.interfaces.go b/crm/types.interfaces.go
new file mode 100644
index 000000000..159a23905
--- /dev/null
+++ b/crm/types.interfaces.go
@@ -0,0 +1,32 @@
+package crm
+
+import (
+ "net/http"
+)
+
+// HTTP handlers are a superset of internal APIs
+type TypesHandlers struct {
+ *Types
+}
+
+func (TypesHandlers) new() *TypesHandlers {
+ return &TypesHandlers{
+ Types{}.new(),
+ }
+}
+
+// Internal API interface
+type TypesAPI interface {
+ List(*typesListRequest) (interface{}, error)
+ Type(*typesTypeRequest) (interface{}, error)
+}
+
+// HTTP API interface
+type TypesHandlersAPI interface {
+ List(http.ResponseWriter, *http.Request)
+ Type(http.ResponseWriter, *http.Request)
+}
+
+// Compile time check to see if we implement the interfaces
+var _ TypesHandlersAPI = &TypesHandlers{}
+var _ TypesAPI = &Types{}
diff --git a/crm/types.request.go b/crm/types.request.go
new file mode 100644
index 000000000..2bd77bf19
--- /dev/null
+++ b/crm/types.request.go
@@ -0,0 +1,56 @@
+package crm
+
+import (
+ "net/http"
+)
+
+// Types list request parameters
+type typesListRequest struct {
+}
+
+func (typesListRequest) new() *typesListRequest {
+ return &typesListRequest{}
+}
+
+func (t *typesListRequest) Fill(r *http.Request) error {
+ get := map[string]string{}
+ post := map[string]string{}
+ urlQuery := r.URL.Query()
+ for name, param := range urlQuery {
+ get[name] = string(param[0])
+ }
+ postVars := r.Form
+ for name, param := range postVars {
+ post[name] = string(param[0])
+ }
+ return nil
+}
+
+var _ RequestFiller = typesListRequest{}.new()
+
+// Types type request parameters
+type typesTypeRequest struct {
+ id string
+}
+
+func (typesTypeRequest) new() *typesTypeRequest {
+ return &typesTypeRequest{}
+}
+
+func (t *typesTypeRequest) Fill(r *http.Request) error {
+ get := map[string]string{}
+ post := map[string]string{}
+ urlQuery := r.URL.Query()
+ for name, param := range urlQuery {
+ get[name] = string(param[0])
+ }
+ postVars := r.Form
+ for name, param := range postVars {
+ post[name] = string(param[0])
+ }
+
+ t.id = path["id"]
+ return nil
+}
+
+var _ RequestFiller = typesTypeRequest{}.new()
diff --git a/crm/types.structs.go b/crm/types.structs.go
new file mode 100644
index 000000000..89601f8f9
--- /dev/null
+++ b/crm/types.structs.go
@@ -0,0 +1,19 @@
+package crm
+
+// Types
+type Types struct {
+ ID string
+}
+
+func (Types) new() *Types {
+ return &Types{}
+}
+
+func (t *Types) GetID() string {
+ return t.ID
+}
+
+func (t *Types) SetID(value string) *Types {
+ t.ID = value
+ return t
+}