├── README.md ├── templates ├── admin_manage.tpl ├── admin_index.tpl ├── client_manage.tpl ├── js │ ├── dataTables.fnReloadAjax.js │ ├── nprogress.min.js │ ├── client.js │ └── admin.js ├── client_manage_dnssec.tpl ├── client_manage_records.tpl ├── admin_template.tpl ├── admin_manage_dnssec.tpl ├── admin_system.tpl ├── css │ └── client.css ├── admin_nameserver.tpl ├── admin_manage_records.tpl └── admin_settings.tpl ├── lib ├── Client │ └── ClientDispatcher.php ├── Admin │ └── AdminDispatcher.php ├── System │ ├── Cron.php │ └── Validate.php ├── Core │ └── config.php └── Vendor │ └── DataTables │ └── SSP.php ├── LICENSE ├── solutedns.php └── hooks.php /README.md: -------------------------------------------------------------------------------- 1 | ************************************************ 2 | 3 | ## SoluteDNS Community Edition for WHMCS 4 | 5 | Copyright (C) NetDistrict 2011-2018 6 | All Rights Reserved 7 | 8 | ************************************************ 9 | 10 | COMPATIBILITY: 11 | ---------------------------------------------- 12 | This module can be used with: 13 | 14 | - WHMCS 15 | - PowerDNS with MySQL Back-end 16 | 17 | Please note the SoluteDNS Core is required for this module. You can find more information about the core at: 18 | - https://www.solutedns.com/solutions/core/ 19 | 20 | Please see for system requirements: 21 | - https://docs.solutedns.com/whmcs/community-edition/system-requirements/ 22 | 23 | FULL PACKAGE: 24 | ---------------------------------------------- 25 | The full package including the SoluteDNS Core is available from: 26 | 27 | - https://forum.solutedns.com/viewtopic.php?f=17&t=125 28 | 29 | DOCUMENTATION: 30 | ---------------------------------------------- 31 | Please see for help the documentation at: 32 | 33 | - https://docs.solutedns.com/whmcs/community-edition/ 34 | 35 | 36 | SUPPORT: 37 | ---------------------------------------------- 38 | Need support? Please go to: 39 | - https://forum.solutedns.com/viewforum.php?f=20 -------------------------------------------------------------------------------- /templates/admin_manage.tpl: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 | 6 | 7 | 14 | 15 | 16 |
17 |
18 |
{include file="{$base_path}/templates/admin_manage_records.tpl"}
19 |
20 | {if Controller::ns_details(dnssec_enable)} 21 |
22 |
{include file="{$base_path}/templates/admin_manage_dnssec.tpl"}
23 |
24 | {/if} 25 |
26 |
27 | 28 | 35 | 36 | 45 | -------------------------------------------------------------------------------- /templates/admin_index.tpl: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 |
5 | 6 | 7 | 14 | 15 | 16 |
17 |
18 |
{include file="{$base_path}/templates/admin_template.tpl"}
19 |
20 |
21 |
{include file="{$base_path}/templates/admin_settings.tpl"}
22 |
23 |
24 |
{include file="{$base_path}/templates/admin_nameserver.tpl"}
25 |
26 |
27 |
{include file="{$base_path}/templates/admin_system.tpl"}
28 |
29 |
30 |
31 | 32 | 39 | 40 | 49 | -------------------------------------------------------------------------------- /templates/client_manage.tpl: -------------------------------------------------------------------------------- 1 | {if $domain->domain eq false} 2 | 5 | {else} 6 |
7 |
8 | 9 |
10 | 11 | 12 | 18 | 19 | 20 |
21 |
22 |
23 | 24 | {include file=".{DIRECTORY_SEPARATOR}client_manage_records.tpl"} 25 |
26 |
27 | {if $dnssec.keys} 28 |
29 |
30 | 31 | {include file=".{DIRECTORY_SEPARATOR}client_manage_dnssec.tpl"} 32 |
33 |
34 | {/if} 35 |
36 |
37 | 38 | 45 | 46 | 55 | {/if} -------------------------------------------------------------------------------- /lib/Client/ClientDispatcher.php: -------------------------------------------------------------------------------- 1 | 28 | * @copyright NetDistrict 29 | * @link https://www.solutedns.com 30 | * */ 31 | if (!defined("WHMCS")) { 32 | die("This file cannot be accessed directly"); 33 | } 34 | 35 | /** 36 | * Client Area Dispatch Handler 37 | */ 38 | class ClientDispatcher { 39 | 40 | /** 41 | * Dispatch request. 42 | * 43 | * @param string $action 44 | * @param array $parameters 45 | * 46 | * @return array 47 | */ 48 | public function dispatch($action, $parameters) { 49 | if (!$action) { 50 | // Default to index if no action specified 51 | $action = 'index'; 52 | } 53 | 54 | $controller = new Controller(); 55 | 56 | // Verify requested action is valid and callable 57 | if (is_callable([$controller, $action])) { 58 | return $controller->$action($parameters); 59 | } 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /lib/Admin/AdminDispatcher.php: -------------------------------------------------------------------------------- 1 | 28 | * @copyright NetDistrict 29 | * @link https://www.solutedns.com 30 | * */ 31 | if (!defined("WHMCS")) { 32 | die("This file cannot be accessed directly"); 33 | } 34 | 35 | /** 36 | * Admin Area Dispatch Handler 37 | */ 38 | class AdminDispatcher { 39 | 40 | /** 41 | * Dispatch request. 42 | * 43 | * @param string $action 44 | * @param array $parameters 45 | * 46 | * @return string 47 | */ 48 | public function dispatch($action, $parameters) { 49 | if (!$action) { 50 | // Default to index if no action specified 51 | $action = 'index'; 52 | } 53 | 54 | $controller = new Controller(); 55 | 56 | // Verify requested action is valid and callable 57 | if (is_callable([$controller, $action])) { 58 | return $controller->$action($parameters); 59 | } 60 | 61 | return '

Invalid action requested. Please go back and try again.

'; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /lib/System/Cron.php: -------------------------------------------------------------------------------- 1 | 28 | * @copyright NetDistrict 29 | * @link https://www.solutedns.com 30 | * */ 31 | if (!defined("WHMCS")) { 32 | die("This file cannot be accessed directly"); 33 | } 34 | 35 | use WHMCS\Database\Capsule; 36 | use WHMCS\Module\Addon\SoluteDNS\Admin\Controller as DNS_Controller; 37 | use solutedns\Dns\Dnssec; 38 | 39 | /** 40 | * Cronjob Handler 41 | */ 42 | class Cron { 43 | 44 | private $dnssec = NULL; 45 | private $session = NULL; 46 | 47 | public function run() { 48 | 49 | // Create Session 50 | $this->dnssec = new Dnssec(); 51 | $this->session = $this->dnssec->session(); 52 | 53 | // Handle Tasks 54 | foreach (Capsule::table('mod_solutedns_cron_queue')->get() as $queue) { 55 | 56 | if ($queue->action == 'rectify') { 57 | 58 | $this->rectify($queue->name); 59 | } 60 | } 61 | 62 | // Update Last Run 63 | $time = time(); 64 | Capsule::table('mod_solutedns_settings')->where('setting', 'last_cron')->update(['value' => $time]); 65 | } 66 | 67 | private function rectify($domain) { 68 | 69 | $result = $this->dnssec->rectify($domain, $this->session); 70 | 71 | if (isset($result['error']) && $result['error']['code'] != '3001') { 72 | 73 | $error = $result['error']; 74 | if (DNS_Controller::Config('logging')) { 75 | logActivity('DNS CRON ERROR [' . $error['code'] . '] for ' . $domain . ': ' . $error['desc'], 0); 76 | } 77 | } else { 78 | 79 | try { 80 | 81 | Capsule::table('mod_solutedns_cron_queue')->where('name', $domain)->where('action', 'rectify')->delete(); 82 | } catch (\Exception $e) { 83 | 84 | if (DNS_Controller::Config('logging')) { 85 | logActivity('DNS CRON ERROR [DB] for ' . $domain . ': ' . $e->getMessage(), 0); 86 | } 87 | } 88 | } 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /templates/js/dataTables.fnReloadAjax.js: -------------------------------------------------------------------------------- 1 | /** 2 | * By default DataTables only uses the sAjaxSource variable at initialisation 3 | * time, however it can be useful to re-read an Ajax source and have the table 4 | * update. Typically you would need to use the `fnClearTable()` and 5 | * `fnAddData()` functions, however this wraps it all up in a single function 6 | * call. 7 | * 8 | * DataTables 1.10 provides the `dt-api ajax.url()` and `dt-api ajax.reload()` 9 | * methods, built-in, to give the same functionality as this plug-in. As such 10 | * this method is marked deprecated, but is available for use with legacy 11 | * version of DataTables. Please use the new API if you are used DataTables 1.10 12 | * or newer. 13 | * 14 | * @name fnReloadAjax 15 | * @summary Reload the table's data from the Ajax source 16 | * @author [Allan Jardine](http://sprymedia.co.uk) 17 | * @deprecated 18 | * 19 | * @param {string} [sNewSource] URL to get the data from. If not give, the 20 | * previously used URL is used. 21 | * @param {function} [fnCallback] Callback that is executed when the table has 22 | * redrawn with the new data 23 | * @param {boolean} [bStandingRedraw=false] Standing redraw (don't changing the 24 | * paging) 25 | * 26 | * @example 27 | * var table = $('#example').dataTable(); 28 | * 29 | * // Example call to load a new file 30 | * table.fnReloadAjax( 'media/examples_support/json_source2.txt' ); 31 | * 32 | * // Example call to reload from original file 33 | * table.fnReloadAjax(); 34 | */ 35 | 36 | jQuery.fn.dataTableExt.oApi.fnReloadAjax = function ( oSettings, sNewSource, fnCallback, bStandingRedraw ) 37 | { 38 | // DataTables 1.10 compatibility - if 1.10 then `versionCheck` exists. 39 | // 1.10's API has ajax reloading built in, so we use those abilities 40 | // directly. 41 | if ( jQuery.fn.dataTable.versionCheck ) { 42 | var api = new jQuery.fn.dataTable.Api( oSettings ); 43 | 44 | if ( sNewSource ) { 45 | api.ajax.url( sNewSource ).load( fnCallback, !bStandingRedraw ); 46 | } 47 | else { 48 | api.ajax.reload( fnCallback, !bStandingRedraw ); 49 | } 50 | return; 51 | } 52 | 53 | if ( sNewSource !== undefined && sNewSource !== null ) { 54 | oSettings.sAjaxSource = sNewSource; 55 | } 56 | 57 | // Server-side processing should just call fnDraw 58 | if ( oSettings.oFeatures.bServerSide ) { 59 | this.fnDraw(); 60 | return; 61 | } 62 | 63 | this.oApi._fnProcessingDisplay( oSettings, true ); 64 | var that = this; 65 | var iStart = oSettings._iDisplayStart; 66 | var aData = []; 67 | 68 | this.oApi._fnServerParams( oSettings, aData ); 69 | 70 | oSettings.fnServerData.call( oSettings.oInstance, oSettings.sAjaxSource, aData, function(json) { 71 | /* Clear the old information from the table */ 72 | that.oApi._fnClearTable( oSettings ); 73 | 74 | /* Got the data - add it to the table */ 75 | var aData = (oSettings.sAjaxDataProp !== "") ? 76 | that.oApi._fnGetObjectDataFn( oSettings.sAjaxDataProp )( json ) : json; 77 | 78 | for ( var i=0 ; i 28 | * @copyright NetDistrict 29 | * @link https://www.solutedns.com 30 | * */ 31 | if (!defined("WHMCS")) { 32 | die("This file cannot be accessed directly"); 33 | } 34 | 35 | use solutedns\Dns\Validate as DNS_Validate; 36 | 37 | /** 38 | * Internal Validation Class 39 | */ 40 | class Validate { 41 | 42 | /** 43 | * Validation request. 44 | * 45 | * @param string $content 46 | * @param string $type 47 | * 48 | * @return bolean 49 | */ 50 | public function input($content, $type) { 51 | 52 | if ($type == 'comsep') { 53 | // 1,2,3 54 | $regex = '/(^\d+(?:,\d+)*$)/'; 55 | } 56 | if ($type == 'license') { 57 | // Abc 123 - _ 58 | $regex = '/(^[A-Za-z0-9\-\_]*$)/'; 59 | } 60 | if ($type == 'check') { 61 | // 1 or on 62 | $regex = '/^(on|1)$/'; 63 | } 64 | if ($type == 'hash') { 65 | // Hash 66 | $regex = '/(^[^<>\'"#]+$)/'; 67 | } 68 | if ($type == 'default') { 69 | // Abc 123 - _ 70 | $regex = '/(^[A-Za-z0-9\-\._ ]*$)/'; 71 | } 72 | if ($type == 'default_ext') { 73 | // Abc 123 - _ <> / : ; . 74 | $regex = '/(^[A-Za-z0-9\-{}<>\/:;\._ ]*$)/'; 75 | } 76 | if ($type == 'int') { 77 | // Integer 78 | $regex = '/(^[0-9-][0-9]*$)/'; 79 | } 80 | if ($type == 'intl') { 81 | // Intiger 1 + 82 | $regex = '/(^[1-9][0-9]*$|^n$)/'; 83 | } 84 | if ($type == 'zonetype') { 85 | // 1 or on 86 | $regex = '/^(MASTER|NATIVE)$/i'; 87 | } 88 | if ($type == 'serial') { 89 | // 1 or on 90 | $regex = '/^(default|epoch|zero)$/i'; 91 | } 92 | if ($type == 'domain') { 93 | // Abc 123 - _ . 94 | $regex = '/(^[A-Za-z0-9\-\._ ]*$)/'; 95 | 96 | // IDN Format 97 | if (extension_loaded('intl')) { 98 | $content = idn_to_ascii($content, 0, INTL_IDNA_VARIANT_UTS46); 99 | } 100 | } 101 | 102 | if ($type == 'domain_limited') { 103 | // Abc 123 - _ . 104 | $regex = '/(^[0-9a-z-]+\.(?:(?:co|or|gv|ac)\.)?[a-z]{2,7}$)/'; 105 | 106 | // IDN Format 107 | if (extension_loaded('intl')) { 108 | $content = idn_to_ascii($content, 0, INTL_IDNA_VARIANT_UTS46); 109 | } 110 | } 111 | 112 | if (is_null($regex)) { 113 | return 'invalid'; 114 | } elseif (is_null($content) || empty($content)) { 115 | return true; 116 | } elseif (preg_match($regex, $content)) { 117 | return true; 118 | } else { 119 | return false; 120 | } 121 | } 122 | 123 | /** 124 | * Template validator. 125 | * 126 | * @param array $input 127 | * 128 | * @return array 129 | */ 130 | public static function template($input) { 131 | 132 | $validate = new DNS_Validate(); 133 | return $validate->pre($input); 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /templates/js/nprogress.min.js: -------------------------------------------------------------------------------- 1 | /* NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress 2 | * @license MIT */ 3 | !function(n,e){"function"==typeof define&&define.amd?define(e):"object"==typeof exports?module.exports=e():n.NProgress=e()}(this,function(){function n(n,e,t){return nt?t:n}function e(n){return 100*(-1+n)}function t(n,t,r){var i;return i="translate3d"===c.positionUsing?{transform:"translate3d("+e(n)+"%,0,0)"}:"translate"===c.positionUsing?{transform:"translate("+e(n)+"%,0)"}:{"margin-left":e(n)+"%"},i.transition="all "+t+"ms "+r,i}function r(n,e){return("string"==typeof n?n:s(n)).indexOf(" "+e+" ")>=0}function i(n,e){var t=s(n),i=t+e;r(t,e)||(n.className=i.substring(1))}function o(n,e){var t,i=s(n);r(n,e)&&(t=i.replace(" "+e+" "," "),n.className=t.substring(1,t.length-1))}function s(n){return(" "+(n&&n.className||"")+" ").replace(/\s+/gi," ")}function a(n){n&&n.parentNode&&n.parentNode.removeChild(n)}var u={};u.version="0.2.0";var c=u.settings={minimum:.08,easing:"linear",positionUsing:"",speed:200,trickle:!0,trickleSpeed:200,showSpinner:!0,barSelector:'[role="bar"]',spinnerSelector:'[role="spinner"]',parent:"body",template:'
'};u.configure=function(n){var e,t;for(e in n)void 0!==(t=n[e])&&n.hasOwnProperty(e)&&(c[e]=t);return this},u.status=null,u.set=function(e){var r=u.isStarted();e=n(e,c.minimum,1),u.status=1===e?null:e;var i=u.render(!r),o=i.querySelector(c.barSelector),s=c.speed,a=c.easing;return i.offsetWidth,l(function(n){""===c.positionUsing&&(c.positionUsing=u.getPositioningCSS()),f(o,t(e,s,a)),1===e?(f(i,{transition:"none",opacity:1}),i.offsetWidth,setTimeout(function(){f(i,{transition:"all "+s+"ms linear",opacity:0}),setTimeout(function(){u.remove(),n()},s)},s)):setTimeout(n,s)}),this},u.isStarted=function(){return"number"==typeof u.status},u.start=function(){u.status||u.set(0);var n=function(){setTimeout(function(){u.status&&(u.trickle(),n())},c.trickleSpeed)};return c.trickle&&n(),this},u.done=function(n){return n||u.status?u.inc(.3+.5*Math.random()).set(1):this},u.inc=function(e){var t=u.status;return t?t>1?void 0:("number"!=typeof e&&(e=t>=0&&t<.2?.1:t>=.2&&t<.5?.04:t>=.5&&t<.8?.02:t>=.8&&t<.99?.005:0),t=n(t+e,0,.994),u.set(t)):u.start()},u.trickle=function(){return u.inc()},function(){var n=0,e=0;u.promise=function(t){return t&&"resolved"!==t.state()?(0===e&&u.start(),n++,e++,t.always(function(){0===--e?(n=0,u.done()):u.set((n-e)/n)}),this):this}}(),u.render=function(n){if(u.isRendered())return document.getElementById("nprogress");i(document.documentElement,"nprogress-busy");var t=document.createElement("div");t.id="nprogress",t.innerHTML=c.template;var r,o=t.querySelector(c.barSelector),s=n?"-100":e(u.status||0),l=document.querySelector(c.parent);return f(o,{transition:"all 0 linear",transform:"translate3d("+s+"%,0,0)"}),c.showSpinner||(r=t.querySelector(c.spinnerSelector))&&a(r),l!=document.body&&i(l,"nprogress-custom-parent"),l.appendChild(t),t},u.remove=function(){o(document.documentElement,"nprogress-busy"),o(document.querySelector(c.parent),"nprogress-custom-parent");var n=document.getElementById("nprogress");n&&a(n)},u.isRendered=function(){return!!document.getElementById("nprogress")},u.getPositioningCSS=function(){var n=document.body.style,e="WebkitTransform"in n?"Webkit":"MozTransform"in n?"Moz":"msTransform"in n?"ms":"OTransform"in n?"O":"";return e+"Perspective"in n?"translate3d":e+"Transform"in n?"translate":"margin"};var l=function(){function n(){var t=e.shift();t&&t(n)}var e=[];return function(t){e.push(t),1==e.length&&n()}}(),f=function(){function n(n){return n.replace(/^-ms-/,"ms-").replace(/-([\da-z])/gi,function(n,e){return e.toUpperCase()})}function e(n){var e=document.body.style;if(n in e)return n;for(var t,r=i.length,o=n.charAt(0).toUpperCase()+n.slice(1);r--;)if((t=i[r]+o)in e)return t;return n}function t(t){return t=n(t),o[t]||(o[t]=e(t))}function r(n,e,r){e=t(e),n.style[e]=r}var i=["Webkit","O","Moz","ms"],o={};return function(n,e){var t,i,o=arguments;if(2==o.length)for(t in e)void 0!==(i=e[t])&&e.hasOwnProperty(t)&&r(n,t,i);else r(n,o[1],o[2])}}();return u}); -------------------------------------------------------------------------------- /lib/Core/config.php: -------------------------------------------------------------------------------- 1 | 26 | * @copyright NetDistrict 27 | * @link https://www.solutedns.com 28 | * */ 29 | 30 | use WHMCS\Module\Addon\SoluteDNS\Admin\Controller; 31 | 32 | $clr = new Controller(); 33 | 34 | // Create Nameserver Array 35 | $nameservers = []; 36 | 37 | if ($clr->ns_details('ns0') !== '') { 38 | $nameservers[] = $clr->ns_details('ns0'); 39 | } 40 | if ($clr->ns_details('ns1') !== '') { 41 | $nameservers[] = $clr->ns_details('ns1'); 42 | } 43 | if ($clr->ns_details('ns2') !== '') { 44 | $nameservers[] = $clr->ns_details('ns2'); 45 | } 46 | if ($clr->ns_details('ns3') !== '') { 47 | $nameservers[] = $clr->ns_details('ns3'); 48 | } 49 | if ($clr->ns_details('ns4') !== '') { 50 | $nameservers[] = $clr->ns_details('ns4'); 51 | } 52 | if ($clr->ns_details('ns5') !== '') { 53 | $nameservers[] = $clr->ns_details('ns5'); 54 | } 55 | 56 | // Configuration 57 | return [ 58 | 'license' => $clr->config('license'), 59 | 'database' => [ 60 | 'host' => $clr->ns_details('db_host'), 61 | 'port' => $clr->ns_details('db_port'), 62 | 'name' => $clr->ns_details('db_name'), 63 | 'user' => $clr->ns_details('db_user'), 64 | 'pass' => $clr->decrypt($clr->ns_details('db_pass')), 65 | 'type' => $clr->ns_details('zone_type') # native/master 66 | ], 67 | 'ssh' => [ 68 | 'host' => $clr->ns_details('ssh_host'), 69 | 'port' => $clr->ns_details('ssh_port'), 70 | 'user' => $clr->ns_details('ssh_user'), 71 | 'pass' => $clr->decrypt($clr->ns_details('ssh_pass')), 72 | 'private_key' => $clr->ns_details('ssh_key'), 73 | 'powerdns_version' => $clr->ns_details('version') # 3/4 74 | ], 75 | 'records' => [ 76 | 'allowed' => (isset($_SESSION['adminid'])) ? $clr->config('record_types').',SOA,NS' : $clr->config('record_types'), 77 | 'limit' => $clr->config('record_limit'), 78 | 'soa' => [ 79 | 'hostmaster' => str_replace(['{', '}'], ':', $clr->config('soa_hostmaster')), 80 | 'serial' => $clr->config('soa_serial'), # default/epoch 81 | 'refresh' => $clr->config('soa_refresh'), 82 | 'retry' => $clr->config('soa_retry'), 83 | 'expire' => $clr->config('soa_expire'), 84 | 'ttl' => $clr->config('soa_ttl') 85 | ], 86 | 'custom_primary' => ($clr->config('custom_primary') !== '' ? true : false), 87 | ], 88 | 'nameservers' => $nameservers, 89 | 'dnssec' => [ 90 | 'enabled' => ($clr->ns_details('dnssec_enable') !== '' ? true : false), 91 | 'auto_rectify' => false, 92 | 'auto_keys' => ($clr->ns_details('dnssec_auto') !== '' ? true : false), 93 | 'auto_nsec3' => ($clr->ns_details('dnssec_nsec3') !== '' ? true : false), 94 | ], 95 | 'health' => [ 96 | 'self_check' => false, 97 | 'consistency_check' => false, 98 | 'record_check' => false, 99 | ], 100 | 'system' => [ 101 | 'debug' => false, 102 | 'regex' => [ 103 | //'VALIDATE_IPV4' => '#[\s\S]#', 104 | //'VALIDATE_IPV6' => '', 105 | //'VALIDATE_FQHN' => '', 106 | //'VALIDATE_FQDN' => '', 107 | //'VALIDATE_TYPES' => '', 108 | //'VALIDATE_QUOTED' => '' 109 | ], 110 | ], 111 | ]; 112 | -------------------------------------------------------------------------------- /templates/client_manage_dnssec.tpl: -------------------------------------------------------------------------------- 1 |
{$MLANG.global_general_dnssec_keys}
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | {if $dnssec.keys} 11 | {foreach from=$dnssec.keys item=key} 12 | 13 | 16 | 43 | 46 | 53 | 56 | 57 | 58 | {/foreach} 59 | {else} 60 | 61 | 62 | 63 | {/if} 64 | 65 | 66 |
67 |
{$MLANG.global_general_dnssec_ds}
68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | {if $dnssec.ds} 77 | {foreach from=$dnssec.ds item=ds} 78 | 79 | 82 | 109 | 122 | 125 | 126 | {/foreach} 127 | {else} 128 | 129 | 130 | 131 | {/if} 132 | -------------------------------------------------------------------------------- /templates/client_manage_records.tpl: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | {if $maintenance eq false} 31 | 32 |
33 | 84 |
85 | 86 | 87 |
88 | 106 |
107 | {/if} -------------------------------------------------------------------------------- /templates/admin_template.tpl: -------------------------------------------------------------------------------- 1 |

2 |
3 |

{$LANG.admin_menu_template}

4 |
5 |
6 |
7 | 8 |
9 | 10 |
11 |
12 |
13 |

14 | 15 | 16 | {assign var=products value=Controller::product_list()} 17 | {assign var=records value=Controller::inConfig(record_types)} 18 | 19 |
20 | 26 |
27 |

{$LANG.global_general_records}

28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 |
{$LANG.global_dns_id}{$LANG.global_dns_name}{$LANG.global_dns_type}{$LANG.global_dns_content}{$LANG.global_dns_prio}{$LANG.global_dns_ttl}
{$LANG.global_general_loading}
48 | 49 | 50 | 51 | 52 |
53 | 104 |
105 | 106 | 107 |
108 | 126 | 127 | -------------------------------------------------------------------------------- /templates/js/client.js: -------------------------------------------------------------------------------- 1 | /********************************************** 2 | 3 | *** SoluteDNS CE for WHMCS *** 4 | 5 | File: template/js/client.js 6 | File version: 0.18.001 7 | Date: 21-04-2018 8 | 9 | Copyright (C) NetDistrict 2016-2018 10 | All Rights Reserved 11 | 12 | **********************************************/ 13 | 14 | function sendData(json) { 15 | 16 | NProgress.start(); 17 | 18 | if (typeof time1 !== 'undefined') { 19 | clearTimeout(time1); 20 | } 21 | if (typeof time2 !== 'undefined') { 22 | clearTimeout(time2); 23 | } 24 | 25 | $.ajax({ 26 | data: { 27 | 'data': json 28 | }, 29 | url: setDataURL() + 'index.php?m=solutedns&action=post', 30 | method: "POST", 31 | cache: false, 32 | success: function (data) { 33 | 34 | var result = JSON.parse(data); 35 | 36 | if (result) { 37 | result.forEach(function (data) { 38 | 39 | setMessage(data['title'], data['msg'], data['status'], data['tablereload'], data['pagereload'], data['fieldreset'], data['msgReset'], data['fixed'], data['errorFields']) 40 | 41 | if (data['syscheck'] == true) { 42 | syscheck(); 43 | } 44 | 45 | if (typeof data['field'] !== 'undefined') { 46 | setErrorField(data['field']); 47 | } 48 | 49 | }); 50 | } 51 | 52 | NProgress.done(); 53 | 54 | }, 55 | error: function () { 56 | alert('Failed to load notifications.\nPlease try to refresh this page.') 57 | }, 58 | 59 | }); 60 | 61 | } 62 | 63 | function setMessage(title, desc, status, tableReload, pageReload, fieldReset, msgReset, fixed, errorFields) { 64 | 65 | /* Message Reset */ 66 | if (msgReset == true) { 67 | resetMessages(); 68 | } 69 | 70 | /* Generate Unique ID */ 71 | var id = '4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { 72 | var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); 73 | return v.toString(16); 74 | }); 75 | 76 | /* Set message state */ 77 | if (status == 'error') { 78 | var state = 'alert2 alert2-danger'; 79 | } else { 80 | var state = 'alert2 alert2-' + status; 81 | } 82 | 83 | /* Add Message */ 84 | $('#msgConsole').append(''); 85 | 86 | /* Display Message */ 87 | $("#" + id).show("slow", function () { 88 | $('html, body').animate({ 89 | scrollTop: $("#msgConsole").offset().top - 10 90 | }, 400); 91 | if (fixed != true) { 92 | setTimeout(function () { 93 | $("#" + id).hide("slow"); 94 | }, 8000); 95 | setTimeout(function () { 96 | $("#" + id).remove(); 97 | }, 8500); 98 | } 99 | }); 100 | 101 | /* Reload Table Date */ 102 | if (tableReload == true) { 103 | SDNS_recordTable.fnReloadAjax(); 104 | } 105 | 106 | /* Reload Page */ 107 | if (pageReload == true) { 108 | setTimeout(function () { 109 | location.reload() 110 | }, 2000); 111 | } 112 | 113 | /* Reset Field Entries */ 114 | if (fieldReset == true) { 115 | clearFields(); 116 | resetDNSField(); 117 | } 118 | 119 | /* Set Error Field */ 120 | setErrorMark(errorFields); 121 | 122 | } 123 | 124 | function resetMessages() { 125 | 126 | /* Clear Message Box */ 127 | $("#msgConsole").empty(); 128 | 129 | /* Retrieve System Messages */ 130 | getState(); 131 | 132 | if ($("#sdns_zone").val().length > 0) { 133 | getZoneState(); 134 | } 135 | 136 | } 137 | 138 | function getState() { 139 | 140 | var zone = $("#sdns_domain").val(); 141 | 142 | var item = { 143 | action: 'systemState', 144 | zone: zone, 145 | }; 146 | 147 | jsonString = JSON.stringify(item); 148 | 149 | sendData(jsonString); 150 | 151 | } 152 | 153 | function setErrorMark(fields) { 154 | 155 | /* Remove previous set errors */ 156 | $(".col-md-1 div").removeClass("has-error"); 157 | $(".col-md-2 div").removeClass("has-error"); 158 | $(".col-md-3 div").removeClass("has-error"); 159 | $(".col-md-9 div").removeClass("has-error"); 160 | 161 | if (fields != null) { 162 | 163 | /* Add error to selected fields */ 164 | var fields = fields.split(","); 165 | 166 | fields.forEach(function (field) { 167 | $("#" + field + "_field").addClass("has-error"); 168 | ; 169 | }); 170 | 171 | } 172 | 173 | } 174 | 175 | function isDataTable(nTable) { 176 | return $.fn.DataTable.fnIsDataTable('#' + nTable); 177 | } 178 | 179 | function edit(id) { 180 | 181 | /* Reset previous selected field */ 182 | resetDNSField(); 183 | 184 | /* Enable current fields */ 185 | $('#sdns_name_' + id).prop('disabled', false); 186 | $('#sdns_type_' + id).prop('disabled', false); 187 | $('#sdns_content_' + id).prop('disabled', false); 188 | $('#sdns_prio_' + id).prop('disabled', false); 189 | $('#sdns_ttl_' + id).prop('disabled', false); 190 | 191 | $('#sdns_edit_' + id).hide(); 192 | $('#sdns_save_' + id).show(); 193 | 194 | setRecord(id); 195 | 196 | } 197 | 198 | function resetDNSField() { 199 | 200 | var id = $("#sdns_record").val(); 201 | 202 | if (id > 1) { 203 | /* Disable previous fields */ 204 | $('#sdns_name_' + id).prop('disabled', true); 205 | $('#sdns_type_' + id).prop('disabled', true); 206 | $('#sdns_content_' + id).prop('disabled', true); 207 | $('#sdns_prio_' + id).prop('disabled', true); 208 | $('#sdns_ttl_' + id).prop('disabled', true); 209 | 210 | $('#sdns_save_' + id).hide(); 211 | $('#sdns_edit_' + id).show(); 212 | } 213 | } 214 | 215 | function deleteSelected() { 216 | 217 | var deleteArray = []; 218 | 219 | $("input:checkbox[name=sdns_select]:checked").each(function () { 220 | deleteArray.push($(this).val()); 221 | }); 222 | 223 | var zone = $("#sdns_zone").val(); 224 | var records = deleteArray; 225 | 226 | var data = { 227 | action: 'deleteselectedrecords', 228 | zone: zone, 229 | records: records 230 | }; 231 | 232 | jsonString = JSON.stringify(data); 233 | 234 | sendData(jsonString); 235 | 236 | } 237 | 238 | function setErrorField(field) { 239 | var record_id = $("#sdns_record").val(); 240 | 241 | $("#sdns_z-" + field + "_" + record_id).addClass("has-error"); 242 | } 243 | 244 | function clearFields() { 245 | $("#sdns_name_0").val(""); 246 | $("#sdns_content_0").val(""); 247 | $("#sdns_prio_0").val(""); 248 | } 249 | 250 | function clearErrorField() { 251 | 252 | var record_id = $("#sdns_record").val(); 253 | 254 | $("#sdns_z-name_" + record_id).removeClass("has-error"); 255 | $("#sdns_z-type_" + record_id).removeClass("has-error"); 256 | $("#sdns_z-content_" + record_id).removeClass("has-error"); 257 | $("#sdns_z-prio_" + record_id).removeClass("has-error"); 258 | $("#sdns_z-ttl_" + record_id).removeClass("has-error"); 259 | 260 | } 261 | 262 | function record_add() { 263 | 264 | setRecord(0); 265 | clearErrorField() 266 | 267 | var zone = $("#sdns_zone").val(); 268 | var var1 = $("#sdns_name_0").val(); 269 | var var2 = $("#sdns_type_0").val(); 270 | var var3 = $("#sdns_content_0").val(); 271 | var var4 = $("#sdns_prio_0").val(); 272 | var var5 = $("#sdns_ttl_0").val(); 273 | 274 | var item = { 275 | action: 'addrecord', 276 | zone: zone, 277 | name: var1, 278 | type: var2, 279 | content: var3, 280 | prio: var4, 281 | ttl: var5 282 | }; 283 | 284 | jsonString = JSON.stringify(item); 285 | 286 | sendData(jsonString); 287 | 288 | } 289 | 290 | function record_edit(record_id) { 291 | 292 | clearErrorField() 293 | 294 | var zone = $("#sdns_zone").val(); 295 | 296 | var var1 = $("#sdns_name_" + record_id).val(); 297 | var var2 = $("#sdns_type_" + record_id).val(); 298 | var var3 = $("#sdns_content_" + record_id).val(); 299 | var var4 = $("#sdns_prio_" + record_id).val(); 300 | var var5 = $("#sdns_ttl_" + record_id).val(); 301 | 302 | var item = { 303 | action: 'editrecord', 304 | zone: zone, 305 | record_id: record_id, 306 | name: var1, 307 | type: var2, 308 | content: var3, 309 | prio: var4, 310 | ttl: var5 311 | }; 312 | 313 | jsonString = JSON.stringify(item); 314 | 315 | sendData(jsonString); 316 | 317 | } 318 | 319 | function record_delete() { 320 | 321 | var zone = $("#sdns_zone").val(); 322 | var record_id = $("#sdns_record").val(); 323 | 324 | var item = { 325 | action: 'deleterecord', 326 | zone: zone, 327 | record_id: record_id 328 | }; 329 | 330 | jsonString = JSON.stringify(item); 331 | 332 | sendData(jsonString); 333 | 334 | } 335 | 336 | function setRecord(id) { 337 | $("#sdns_record").val(id); 338 | } 339 | 340 | function setDataURL() { 341 | 342 | var lastChar = location.pathname.substr(location.pathname.length - 1); 343 | 344 | if (lastChar.search(/^\s*\d+\s*$/) != -1) { 345 | 346 | var pathName = location.pathname.substr(0, location.pathname.lastIndexOf("\/")); 347 | pathName = pathName.substr(0, pathName.lastIndexOf("\/")); 348 | 349 | } else { 350 | var pathName = location.pathname.substr(0, location.pathname.lastIndexOf("\/")); 351 | 352 | } 353 | 354 | return location.protocol + '//' + location.host + pathName + '/'; 355 | 356 | } 357 | 358 | $(document).ready(function () { 359 | 360 | /* Javascript to enable link to tab */ 361 | var url = document.location.toString(); 362 | if (url.match('#')) { 363 | $('.nav-tabs a[href="#' + url.split('#')[1] + '"]').tab('show'); 364 | var atab = url.split('#')[1]; 365 | } 366 | 367 | /* HTML5 Prevent scrolling! */ 368 | $('.nav-tabs a').on('shown.bs.tab', function (e) { 369 | if (history.pushState) { 370 | history.pushState(null, null, e.target.hash); 371 | } else { 372 | window.location.hash = e.target.hash; //Polyfill for old browsers 373 | } 374 | }) 375 | 376 | /* Activate tooltip */ 377 | $(function () { 378 | $('[data-toggle="tooltip"]').tooltip({container: 'body', html: true, 379 | 380 | template: '' 381 | 382 | }) 383 | }) 384 | 385 | }); -------------------------------------------------------------------------------- /templates/admin_manage_dnssec.tpl: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{$LANG.admin_manage_title_zone}: 4 |
{$domain->domain} {if $dnssec.nsec} {$dnssec.nsec}{/if}

5 |
6 |
7 |
8 | 9 |
10 | 11 |
12 | 13 | 14 | 27 | 28 | 29 |
30 |
31 |
32 | 33 |

{$LANG.global_general_dnssec_keys}

34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | {if $dnssec.keys} 46 | {foreach from=$dnssec.keys item=key} 47 | 48 | 51 | 54 | 57 | 84 | 87 | 94 | 102 | 103 | 104 | 105 | {/foreach} 106 | {else} 107 | 108 | 109 | 110 | {/if} 111 |
{$LANG.global_dns_id}{$LANG.global_dns_keytag}{$LANG.global_dns_flag}{$LANG.global_dns_algorithm}{$LANG.global_dns_publickey}{$LANG.global_dns_status}
49 | {$key.id} 50 | 52 | {$key.key_tag} 53 | 55 | {$key.flag} 56 | 58 | {if $key.algorithm eq '1'} 59 | RSA/MD5 (1) 60 | {elseif $key.algorithm eq '2'} 61 | Diffie-Hellman (2) 62 | {elseif $key.algorithm eq '3'} 63 | DSA/SHA1 (3) 64 | {elseif $key.algorithm eq '5'} 65 | RSA/SHA-1 (5) 66 | {elseif $key.algorithm eq '6'} 67 | DSA-NSEC3-SHA1 (6) 68 | {elseif $key.algorithm eq '7'} 69 | RSASHA1-NSEC3-SHA1 (7) 70 | {elseif $key.algorithm eq '8'} 71 | RSA/SHA-256 (8) 72 | {elseif $key.algorithm eq '10'} 73 | RSA/SHA-512 (10) 74 | {elseif $key.algorithm eq '12'} 75 | GOST R 34.10-2001 (12) 76 | {elseif $key.algorithm eq '13'} 77 | ECDSA Curve P-256 with SHA-256 (13) 78 | {elseif $key.algorithm eq '14'} 79 | ECDSA Curve P-384 with SHA-384 (14) 80 | {else} 81 | {$LANG.global_general_unknown} 82 | {/if} 83 | 85 | 86 | 88 | {if $key.active eq '1'} 89 | active 90 | {else} 91 | inactive 92 | {/if} 93 | 95 | {if $key.active eq '0'} 96 | 97 | 98 | {else} 99 | 100 | {/if} 101 |
{$LANG.global_status_noneavailable}
112 | 113 |
114 |

{$LANG.global_general_dnssec_ds}

115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | {if $dnssec.ds} 124 | {foreach from=$dnssec.ds item=ds} 125 | 126 | 129 | 156 | 169 | 172 | 173 | {/foreach} 174 | {else} 175 | 176 | 177 | 178 | {/if} 179 |
{$LANG.global_dns_keytag}{$LANG.global_dns_algorithm}{$LANG.global_dns_digesttype}{$LANG.global_dns_digest}
127 | {$ds.key_tag} 128 | 130 | {if $ds.algorithm eq '1'} 131 | RSA/MD5 (1) 132 | {elseif $ds.algorithm eq '2'} 133 | Diffie-Hellman (2) 134 | {elseif $ds.algorithm eq '3'} 135 | DSA/SHA1 (3) 136 | {elseif $ds.algorithm eq '5'} 137 | RSA/SHA-1 (5) 138 | {elseif $ds.algorithm eq '6'} 139 | DSA-NSEC3-SHA1 (6) 140 | {elseif $ds.algorithm eq '7'} 141 | RSASHA1-NSEC3-SHA1 (7) 142 | {elseif $ds.algorithm eq '8'} 143 | RSA/SHA-256 (8) 144 | {elseif $ds.algorithm eq '10'} 145 | RSA/SHA-512 (10) 146 | {elseif $ds.algorithm eq '12'} 147 | GOST R 34.10-2001 (12) 148 | {elseif $ds.algorithm eq '13'} 149 | ECDSA Curve P-256 with SHA-256 (13) 150 | {elseif $ds.algorithm eq '14'} 151 | ECDSA Curve P-384 with SHA-384 (14) 152 | {else} 153 | {$LANG.admin_manage_unknown} 154 | {/if} 155 | 157 | {if $ds.digest_type eq '1'} 158 | SHA-1 (1) 159 | {elseif $ds.digest_type eq '2'} 160 | SHA-256 (2) 161 | {elseif $ds.digest_type eq '3'} 162 | GOST R 34.11-94 (3) 163 | {elseif $ds.digest_type eq '4'} 164 | SHA-384 (4) 165 | {else} 166 | {$LANG.admin_manage_unknown} 167 | {/if} 168 | 170 | 171 |
{$LANG.global_status_noneavailable}
180 | 181 | 182 |
183 | 228 |
-------------------------------------------------------------------------------- /templates/admin_system.tpl: -------------------------------------------------------------------------------- 1 | {assign var=core value=Controller::core()} 2 | 3 |

{$LANG.admin_menu_system}

4 | 5 |
6 |
7 | 8 | 9 |
10 |
11 |

{$LANG.admin_system_module_version}:

12 |
13 |
14 |

{Controller::version()}

15 |
16 |
17 | 18 |
19 |
20 |

{$LANG.admin_system_core_version}:

21 |
22 |
23 |

{$core.version}

24 |
25 |
26 | 27 |
28 |
29 |

{$LANG.admin_system_idn_support}:

30 |
31 |
32 |

{if Controller::idnCheck() eq true}{$LANG.global_status_enabled}{else}{$LANG.global_status_disabled}{/if}

33 |
34 |
35 | 36 |
37 |
38 |

{$LANG.admin_system_cron_status}:

39 |
40 |
41 |

{$LANG.admin_system_cron_status_tasks} {$cron_queue} | {$LANG.admin_system_cron_status_run} {if Controller::config(last_cron)}{Controller::config(last_cron)|date_format:"%B %e, %Y (%T)"}{else}{$LANG.admin_never}{/if} 42 |

43 |
44 | 45 |
46 |
47 | 48 |
49 |
50 |
51 | 52 | 53 |
54 |
55 |
56 | 57 |
58 |
59 | 60 |
61 |
62 |
63 | 64 | 65 |
66 |
67 |
68 | 69 |
70 |

{$LANG.admin_system_license}

71 | 72 |
73 |
74 |

{$LANG.admin_system_license_product}:

75 |
76 |
77 |

SoluteDNS for WHMCS

78 |
79 |
80 | 81 |
82 | 83 |
84 |

{$LANG.admin_system_license_edition}:

85 |

86 |
87 |

Community Edition

88 |
89 |
90 | 91 |
92 |
93 |

{$LANG.admin_system_license_status}:

94 |
95 |
96 |

Active

97 |
98 |
99 | 100 |
101 |
102 |

{$LANG.admin_system_license_expires}:

103 |
104 |
105 |

{$LANG.admin_never}

106 |
107 |
108 | 109 |
110 |
111 |
112 |
113 |
114 |
115 | 116 | {if $core.license} 117 | {if $core.license.status eq 'Invalid'} 118 |
119 |
120 |

{$LANG.admin_system_license_product}:

121 |
122 |
123 |

SoluteDNS Core

124 |
125 |
126 | 127 |
128 |
129 |
130 | 131 |
132 |
133 |

License {$core.license.status}

134 |

Your license key is invalid which may be caused by several reasons including:

- An incorrect license key has been entered.
- The domain to access this installation has changed.
- The IP of this installation has changed.
- The installation directory has changed.

135 |
136 |
137 |

You can reissue your license key on-demand from our client area when required, or enter a different license key below.
If you are not sure what caused this error, please contact support for assistance! If you did not enter a license before please submit the provided license key.

138 | 139 |
140 |
141 | {elseif $core.license.status eq 'expired'} 142 |
143 |
144 |

{$LANG.admin_system_license_product}:

145 |
146 |
147 |

SoluteDNS Core

148 |
149 |
150 | 151 |
152 |
153 |
154 | 155 |
156 |
157 |

License Expired

158 |

The license key you are using has been expired. To continue using SoluteDNS you need to renew your subscription.

159 |
160 |
161 | 162 |
163 |
164 | {elseif $core.license.status eq 'error'} 165 |
166 |
167 |

{$LANG.admin_system_license_product}:

168 |
169 |
170 |

SoluteDNS Core

171 |
172 |
173 | 174 |
175 |
176 |
177 | 178 |
179 |
180 |

License Error

181 |

An error occurred when processing the license data. Please make sure all required PowerDNS tables exist.

182 |
183 |
184 | 185 |
186 |
187 | {elseif $core.license.status eq 'database'} 188 |
189 |
190 |

{$LANG.admin_system_license_product}:

191 |
192 |
193 |

SoluteDNS Core

194 |
195 |
196 | 197 |
198 |
199 |
200 | 201 |
202 |
203 |

License Unavailable

204 |

Unable to retrieve license information. Is the SoluteDNS Core connected to the nameserver database?

205 |
206 |
207 | 208 |
209 |
210 | {else} 211 |
212 |
213 |

{$LANG.admin_system_license_product}:

214 |
215 |
216 |

{$core.license.productname}

217 |
218 |
219 | 220 |
221 |
222 |

{$LANG.admin_system_license_status}:

223 |
224 |
225 |

{if $core.license.status eq 'Active'} {$core.license.status} {elseif $core.license.status eq 'Pending'} {$core.license.status} {else} {$core.license.status} {/if}

226 |
227 |
228 | 229 |
230 |
231 |

{$LANG.admin_system_license_expires}:

232 |
233 |
234 |

{if $core.license.nextduedate eq '0000-00-00'} {$LANG.admin_never} {else} {$core.license.nextduedate} {/if}

235 |
236 |
237 | 238 |
239 |
240 |

{$LANG.admin_system_license_licensed_to}:

241 |
242 |
243 |

{$core.license.companyname} 244 |
245 | {$core.license.registeredname}

246 |
247 |
248 | 249 |
250 |
251 |

{$LANG.admin_system_license_limit}:

252 |
253 |
254 |

{if $core.license.zonelimit}{if {$core.license.zonelimit} eq '0'}{$LANG.admin_system_license_unlimited}{else}{$core.license.zonelimit} {$LANG.admin_system_license_zones}{/if}{else}{$LANG.global_status_na}{/if}

255 |
256 |
257 | 258 |
259 |
260 |

{$LANG.admin_system_license_addons}:

261 |
262 |
263 |

264 | {if $core.license.addon} {foreach from=$core.license.addon item=addon} 265 |

- {$addon.name} {if $addon.status eq 'Active'} {$addon.status} {elseif $license_addons.status eq 'Active'} {$addon.status} {else} {$addon.status} {/if} {$addon.duedate} 266 |

267 | {/foreach} {else} {$LANG.global_status_none} {/if} 268 |

269 |
270 |
271 | 272 |
273 |
274 |

{$LANG.admin_system_license_valid_domains}:

275 |
276 |
277 |

{if $core.license.validdomain}{$core.license.validdomain}{else}{$LANG.global_status_na}{/if}

278 |
279 |
280 | 281 |
282 |
283 |

{$LANG.admin_system_license_valid_ips}:

284 |
285 |
286 |

{if $core.license.validip}{$core.license.validip}{else}{$LANG.global_status_na}{/if}

287 |
288 |
289 | 290 |
291 |
292 |

{$LANG.admin_system_license_valid_directory}:

293 |
294 |
295 |

{$core.license.validdirectory}

296 |
297 |
298 | {/if} 299 | {else} 300 |
301 |
302 |
303 |

{$LANG.admin_system_core_not_loaded}

304 |
305 |
306 | {/if} 307 | 308 |
309 |
310 | 311 |
312 |
313 | 314 |
315 |
316 |
317 | 318 |
319 |
320 | 321 |
322 | 323 |
324 |
325 | -------------------------------------------------------------------------------- /templates/css/client.css: -------------------------------------------------------------------------------- 1 | /********************************************** 2 | 3 | *** SoluteDNS CE for WHMCS *** 4 | 5 | File: template/css/client.css 6 | File version: 0.17.001 7 | Date: 09-11-2017 8 | 9 | Copyright (C) NetDistrict 2013-2017 10 | All Rights Reserved 11 | 12 | **********************************************/ 13 | 14 | /* 15 | ## General CSS 16 | */ 17 | h4 .label { 18 | font-size: 60%; 19 | } 20 | .nav-tabs .title { 21 | position: absolute; 22 | } 23 | 24 | /* 25 | ## Loading CSS 26 | */ 27 | .data_spinner { 28 | width: 100px; 29 | text-align: center; 30 | display: inline-block; 31 | } 32 | .data_spinner > div { 33 | width: 18px; 34 | height: 18px; 35 | background-color: #333; 36 | border-radius: 100%; 37 | display: inline-block; 38 | -webkit-animation: sk-bouncedelay 1.4s infinite ease-in-out both; 39 | animation: sk-bouncedelay 1.4s infinite ease-in-out both; 40 | margin-left: 7px; 41 | margin-right: 7px; 42 | } 43 | .data_spinner .bounce1 { 44 | -webkit-animation-delay: -0.32s; 45 | animation-delay: -0.32s; 46 | } 47 | .data_spinner .bounce2 { 48 | -webkit-animation-delay: -0.16s; 49 | animation-delay: -0.16s; 50 | } 51 | @-webkit-keyframes sk-bouncedelay { 52 | 0%, 80%, 100% { 53 | -webkit-transform: scale(0) 54 | } 55 | 40% { 56 | -webkit-transform: scale(1.0) 57 | } 58 | } 59 | @keyframes sk-bouncedelay { 60 | 0%, 80%, 100% { 61 | -webkit-transform: scale(0); 62 | transform: scale(0); 63 | } 64 | 40% { 65 | -webkit-transform: scale(1.0); 66 | transform: scale(1.0); 67 | } 68 | } 69 | 70 | /* 71 | ## Checkboxes Buttons 72 | */ 73 | .checkbox { 74 | padding-left: 6px; 75 | } 76 | .tablecheckbox { 77 | padding-top: 9px; 78 | padding-left: 8px; 79 | width: 100%; 80 | margin-left: 50%; 81 | } 82 | .checkbox label { 83 | display: inline-block; 84 | position: relative; 85 | padding-left: 5px; 86 | } 87 | .checkbox label::before { 88 | content: ""; 89 | display: inline-block; 90 | position: absolute; 91 | width: 17px; 92 | height: 17px; 93 | left: 0; 94 | margin-left: -30px; 95 | border: 1px solid #cccccc; 96 | border-radius: 3px; 97 | background-color: #fff; 98 | -webkit-transition: border 0.15s ease-in-out, color 0.15s ease-in-out; 99 | -o-transition: border 0.15s ease-in-out, color 0.15s ease-in-out; 100 | transition: border 0.15s ease-in-out, color 0.15s ease-in-out; 101 | } 102 | .checkbox label::after { 103 | display: inline-block; 104 | position: absolute; 105 | width: 16px; 106 | height: 16px; 107 | left: 0; 108 | top: 0; 109 | margin-left: -30px; 110 | padding-left: 3px; 111 | padding-top: 1px; 112 | font-size: 11px; 113 | color: #555555; 114 | } 115 | .checkbox input[type="checkbox"] { 116 | opacity: 0; 117 | } 118 | .checkbox input[type="checkbox"]:focus + label::before { 119 | outline: thin dotted; 120 | outline: 5px auto -webkit-focus-ring-color; 121 | outline-offset: -2px; 122 | } 123 | .checkbox input[type="checkbox"]:checked + label::after { 124 | font-family: 'Font Awesome 5 Pro', 'FontAwesome'; 125 | content: "\f00c"; 126 | } 127 | .checkbox input[type="checkbox"]:disabled + label { 128 | opacity: 0.65; 129 | } 130 | .checkbox input[type="checkbox"]:disabled + label::before { 131 | background-color: #eeeeee; 132 | cursor: not-allowed; 133 | } 134 | .checkbox.checkbox-circle label::before { 135 | border-radius: 50%; 136 | } 137 | .checkbox.checkbox-inline { 138 | margin-top: 0; 139 | } 140 | .checkbox-primary input[type="checkbox"]:checked + label::before { 141 | background-color: #428bca; 142 | border-color: #428bca; 143 | } 144 | .checkbox-primary input[type="checkbox"]:checked + label::after { 145 | color: #fff; 146 | } 147 | .checkbox-danger input[type="checkbox"]:checked + label::before { 148 | background-color: #d9534f; 149 | border-color: #d9534f; 150 | } 151 | .checkbox-danger input[type="checkbox"]:checked + label::after { 152 | color: #fff; 153 | } 154 | .checkbox-info input[type="checkbox"]:checked + label::before { 155 | background-color: #5bc0de; 156 | border-color: #5bc0de; 157 | } 158 | .checkbox-info input[type="checkbox"]:checked + label::after { 159 | color: #fff; 160 | } 161 | .checkbox-warning input[type="checkbox"]:checked + label::before { 162 | background-color: #f0ad4e; 163 | border-color: #f0ad4e; 164 | } 165 | .checkbox-warning input[type="checkbox"]:checked + label::after { 166 | color: #fff; 167 | } 168 | .checkbox-success input[type="checkbox"]:checked + label::before { 169 | background-color: #5cb85c; 170 | border-color: #5cb85c; 171 | } 172 | .checkbox-success input[type="checkbox"]:checked + label::after { 173 | color: #fff; 174 | } 175 | 176 | /* 177 | ## Bootstrap 178 | */ 179 | .tooltip-inner { 180 | max-width: none; 181 | white-space: nowrap; 182 | } 183 | .dropmenu_desc { 184 | margin-left: 10px; 185 | } 186 | .dnsfield { 187 | width: 100% !important; 188 | } 189 | 190 | /* 191 | ## NProgress Bar 192 | */ 193 | 194 | /* Make clicks pass-through */ 195 | #nprogress { 196 | pointer-events: none; 197 | } 198 | #nprogress .bar { 199 | background: #29d; 200 | position: fixed; 201 | z-index: 1031; 202 | top: 0; 203 | left: 0; 204 | width: 100%; 205 | height: 3px; 206 | } 207 | 208 | /* Fancy blur effect */ 209 | #nprogress .peg { 210 | display: block; 211 | position: absolute; 212 | right: 0px; 213 | width: 100px; 214 | height: 100%; 215 | box-shadow: 0 0 10px #29d, 0 0 5px #29d; 216 | opacity: 0.5; 217 | -webkit-transform: rotate(1deg) translate(0px, 0px); 218 | -ms-transform: rotate(1deg) translate(0px, 0px); 219 | transform: rotate(1deg) translate(0px, 0px); 220 | } 221 | 222 | /* Remove these to get rid of the spinner */ 223 | #nprogress .spinner { 224 | display: block; 225 | position: fixed; 226 | z-index: 1031; 227 | top: 15px; 228 | right: 15px; 229 | } 230 | #nprogress .spinner-icon { 231 | width: 18px; 232 | height: 18px; 233 | box-sizing: border-box; 234 | border: solid 2px transparent; 235 | border-top-color: #29d; 236 | border-left-color: #29d; 237 | border-radius: 50%; 238 | -webkit-animation: nprogress-spinner 400ms linear infinite; 239 | animation: nprogress-spinner 400ms linear infinite; 240 | } 241 | .nprogress-custom-parent { 242 | overflow: hidden; 243 | position: relative; 244 | } 245 | .nprogress-custom-parent #nprogress .spinner, .nprogress-custom-parent #nprogress .bar { 246 | position: absolute; 247 | } 248 | @-webkit-keyframes nprogress-spinner { 249 | 0% { 250 | -webkit-transform: rotate(0deg); 251 | } 252 | 100% { 253 | -webkit-transform: rotate(360deg); 254 | } 255 | } 256 | @keyframes nprogress-spinner { 257 | 0% { 258 | transform: rotate(0deg); 259 | } 260 | 100% { 261 | transform: rotate(360deg); 262 | } 263 | } 264 | 265 | /* 266 | ## Alert Boxes 267 | */ 268 | .alert2 { 269 | padding: 18px; 270 | margin: 10px 0; 271 | border: 1px solid #eee; 272 | border-left-width: 5px; 273 | border-radius: 3px; 274 | background-color: #FDFDFD; 275 | } 276 | .alert2 h4 { 277 | margin-top: 0; 278 | margin-bottom: 5px; 279 | padding: 0; 280 | } 281 | .alert2 p:last-child { 282 | margin-bottom: 0; 283 | font-size: 12px; 284 | line-height: 1.42857; 285 | color: #333; 286 | padding: 0; 287 | } 288 | .alert2 code { 289 | border-radius: 3px 290 | } 291 | .alert2 { 292 | margin-top: 5px 293 | } 294 | .alert2-danger { 295 | border-color: #F9E8E8; 296 | border-left-color: #c60f13 297 | } 298 | .alert2-danger h4 { 299 | color: #c60f13 300 | } 301 | .msg .alert2-danger p { 302 | font-weight: bold; 303 | color: #c60f13 304 | } 305 | .alert2-warning { 306 | border-color: #FBEDDB; 307 | border-left-color: #e3b000 308 | } 309 | .alert2-warning h4 { 310 | color: #e3b000 311 | } 312 | .msg .alert2-warning p { 313 | font-weight: bold; 314 | color: #e3b000 315 | } 316 | .alert2-info { 317 | border-color: #D6E3F8; 318 | border-left-color: #2ba6cb 319 | } 320 | .alert2-info h4 { 321 | color: #2ba6cb 322 | } 323 | .msg .alert2-info p { 324 | font-weight: bold; 325 | color: #2ba6cb 326 | } 327 | .alert2-success { 328 | border-color: #C7D7C4; 329 | border-left-color: #5da423; 330 | } 331 | .alert2-success h4 { 332 | color: #5da423 333 | } 334 | .msg .alert2-success p { 335 | font-weight: bold; 336 | color: #5da423 337 | } 338 | 339 | /* 340 | ## Control feature layout 341 | */ 342 | .dataTables_wrapper .dataTables_processing { 343 | position: absolute; 344 | top: 50%; 345 | left: 50%; 346 | width: 100%; 347 | height: 80px; 348 | margin-left: -50%; 349 | margin-top: 0px !important; 350 | text-align: center; 351 | color: #808080 !important; 352 | font-size: 1.2em; 353 | line-height: 80px; 354 | border: 0px; 355 | box-shadow: none; 356 | background-color: white; 357 | background: -webkit-radial-gradient(center, rgba(255, 255, 255, 1) 0%, rgba(255, 255, 255, 0.9) 1%, rgba(255, 255, 255, 0.9)4%, rgba(255, 255, 255, 0) 60%); 358 | /* Chrome10+,Safari5.1+ */ 359 | background: -moz-radial-gradient(center, rgba(255, 255, 255, 1) 1%, rgba(255, 255, 255, 0.9) 1%, rgba(255, 255, 255, 0.9) 4%, rgba(255, 255, 255, 0) 60%); 360 | /* FF3.6+ */ 361 | background: -ms-radial-gradient(center, rgba(255, 255, 255, 1) 1%, rgba(255, 255, 255, 0.9) 1%, rgba(255, 255, 255, 0.9) 4%, rgba(255, 255, 255, 0) 60%); 362 | /* IE10+ */ 363 | background: -o-radial-gradient(at center, rgba(255, 255, 255, 1) 1%, rgba(255, 255, 255, 0.9) 1%, rgba(255, 255, 255, 0.9) 4%, rgba(255, 255, 255, 0) 60%); 364 | /* Opera 11.10+ */ 365 | background: radial-gradient(center, rgba(255, 255, 255, 1) 1%, rgba(255, 255, 255, 0.9) 1%, rgba(255, 255, 255, 0.9) 4%, rgba(255, 255, 255, 0) 60%);/* W3C */ 366 | } 367 | 368 | /* 369 | ## Responsive Table styles 370 | */ 371 | table.dataTable.dtr-inline.collapsed tbody td:first-child, table.dataTable.dtr-inline.collapsed tbody th:first-child { 372 | position: relative; 373 | padding-left: 30px; 374 | cursor: pointer; 375 | } 376 | table.dataTable.dtr-inline.collapsed tbody td:first-child:before, table.dataTable.dtr-inline.collapsed tbody th:first-child:before { 377 | top: 8px; 378 | left: 4px; 379 | height: 16px; 380 | width: 16px; 381 | display: block; 382 | position: absolute; 383 | color: white; 384 | border: 2px solid white; 385 | border-radius: 16px; 386 | text-align: center; 387 | line-height: 14px; 388 | box-shadow: 0 0 3px #444; 389 | box-sizing: content-box; 390 | content: '+'; 391 | background-color: #31b131; 392 | } 393 | table.dataTable.dtr-inline.collapsed tbody td:first-child.dataTables_empty:before, table.dataTable.dtr-inline.collapsed tbody th:first-child.dataTables_empty:before { 394 | display: none; 395 | } 396 | table.dataTable.dtr-inline.collapsed tbody tr.parent td:first-child:before, table.dataTable.dtr-inline.collapsed tbody tr.parent th:first-child:before { 397 | content: '-'; 398 | background-color: #d33333; 399 | } 400 | table.dataTable.dtr-inline.collapsed tbody tr.child td:before { 401 | display: none; 402 | } 403 | table.dataTable.dtr-column tbody td.control, table.dataTable.dtr-column tbody th.control { 404 | position: relative; 405 | cursor: pointer; 406 | } 407 | table.dataTable.dtr-column tbody td.control:before, table.dataTable.dtr-column tbody th.control:before { 408 | top: 50%; 409 | left: 50%; 410 | height: 16px; 411 | width: 16px; 412 | margin-top: -10px; 413 | margin-left: -10px; 414 | display: block; 415 | position: absolute; 416 | color: white; 417 | border: 2px solid white; 418 | border-radius: 16px; 419 | text-align: center; 420 | line-height: 14px; 421 | box-shadow: 0 0 3px #444; 422 | box-sizing: content-box; 423 | content: '+'; 424 | background-color: #31b131; 425 | } 426 | table.dataTable.dtr-column tbody tr.parent td.control:before, table.dataTable.dtr-column tbody tr.parent th.control:before { 427 | content: '-'; 428 | background-color: #d33333; 429 | } 430 | table.dataTable tr.child { 431 | padding: 0.5em 1em; 432 | } 433 | table.dataTable tr.child:hover { 434 | background: transparent !important; 435 | } 436 | table.dataTable tr.child ul { 437 | display: inline-block; 438 | list-style-type: none; 439 | margin: 0; 440 | padding: 0; 441 | } 442 | table.dataTable tr.child ul li { 443 | border-bottom: 1px solid #efefef; 444 | padding: 0.5em 0; 445 | white-space: nowrap; 446 | } 447 | table.dataTable tr.child ul li:first-child { 448 | padding-top: 0; 449 | } 450 | table.dataTable tr.child ul li:last-child { 451 | border-bottom: none; 452 | } 453 | table.dataTable tr.child span.dtr-title { 454 | display: inline-block; 455 | min-width: 75px; 456 | font-weight: bold; 457 | } 458 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | SoluteDNS Community Edition for WHMCS 2 | -------------------------------------------------------------------------------- 3 | The Public License Agreement mentioned below applies as written permission in 4 | addition to the SoluteDNS End User License Agreement as found at: 5 | https://www.solutedns.com/eula/ and is limited to the accompanying program only. 6 | This Public License Agreement may not apply to third-party/vendor programs 7 | included and is not applicable to other programs who may be required for this 8 | product to operate. 9 | -------------------------------------------------------------------------------- 10 | 11 | Eclipse Public License - v 1.0 12 | 13 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC 14 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM 15 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 16 | 17 | 1. DEFINITIONS 18 | 19 | "Contribution" means: 20 | a) in the case of the initial Contributor, the initial code and 21 | documentation distributed under this Agreement, and 22 | b) in the case of each subsequent Contributor: 23 | i) changes to the Program, and 24 | ii) additions to the Program; 25 | 26 | where such changes and/or additions to the Program originate from and are 27 | distributed by that particular Contributor. A Contribution 'originates' from a 28 | Contributor if it was added to the Program by such Contributor itself or 29 | anyone acting on such Contributor's behalf. Contributions do not include 30 | additions to the Program which: (i) are separate modules of software 31 | distributed in conjunction with the Program under their own license agreement, 32 | and (ii) are not derivative works of the Program. 33 | "Contributor" means any person or entity that distributes the Program. 34 | 35 | "Licensed Patents" mean patent claims licensable by a Contributor which are 36 | necessarily infringed by the use or sale of its Contribution alone or when 37 | combined with the Program. 38 | 39 | "Program" means the Contributions distributed in accordance with this 40 | Agreement. 41 | 42 | "Recipient" means anyone who receives the Program under this Agreement, 43 | including all Contributors. 44 | 45 | 2. GRANT OF RIGHTS 46 | 47 | a) Subject to the terms of this Agreement, each Contributor hereby grants 48 | Recipient a non-exclusive, worldwide, royalty-free copyright license to 49 | reproduce, prepare derivative works of, publicly display, publicly 50 | perform, distribute and sublicense the Contribution of such Contributor, 51 | if any, and such derivative works, in source code and object code form. 52 | 53 | b) Subject to the terms of this Agreement, each Contributor hereby grants 54 | Recipient a non-exclusive, worldwide, royalty-free patent license under 55 | Licensed Patents to make, use, sell, offer to sell, import and otherwise 56 | transfer the Contribution of such Contributor, if any, in source code and 57 | object code form. This patent license shall apply to the combination of 58 | the Contribution and the Program if, at the time the Contribution is 59 | added by the Contributor, such addition of the Contribution causes such 60 | combination to be covered by the Licensed Patents. The patent license 61 | shall not apply to any other combinations which include the Contribution. 62 | No hardware per se is licensed hereunder. 63 | 64 | c) Recipient understands that although each Contributor grants the 65 | licenses to its Contributions set forth herein, no assurances are 66 | provided by any Contributor that the Program does not infringe the patent 67 | or other intellectual property rights of any other entity. Each 68 | Contributor disclaims any liability to Recipient for claims brought by 69 | any other entity based on infringement of intellectual property rights or 70 | otherwise. As a condition to exercising the rights and licenses granted 71 | hereunder, each Recipient hereby assumes sole responsibility to secure 72 | any other intellectual property rights needed, if any. For example, if a 73 | third party patent license is required to allow Recipient to distribute 74 | the Program, it is Recipient's responsibility to acquire that license 75 | before distributing the Program. 76 | 77 | d) Each Contributor represents that to its knowledge it has sufficient 78 | copyright rights in its Contribution, if any, to grant the copyright 79 | license set forth in this Agreement. 80 | 81 | 3. REQUIREMENTS 82 | A Contributor may choose to distribute the Program in object code form under 83 | its own license agreement, provided that: 84 | 85 | a) it complies with the terms and conditions of this Agreement; and 86 | 87 | b) its license agreement: 88 | i) effectively disclaims on behalf of all Contributors all 89 | warranties and conditions, express and implied, including warranties 90 | or conditions of title and non-infringement, and implied warranties 91 | or conditions of merchantability and fitness for a particular 92 | purpose; 93 | ii) effectively excludes on behalf of all Contributors all liability 94 | for damages, including direct, indirect, special, incidental and 95 | consequential damages, such as lost profits; 96 | iii) states that any provisions which differ from this Agreement are 97 | offered by that Contributor alone and not by any other party; and 98 | iv) states that source code for the Program is available from such 99 | Contributor, and informs licensees how to obtain it in a reasonable 100 | manner on or through a medium customarily used for software 101 | exchange. 102 | 103 | When the Program is made available in source code form: 104 | 105 | a) it must be made available under this Agreement; and 106 | 107 | b) a copy of this Agreement must be included with each copy of the 108 | Program. 109 | Contributors may not remove or alter any copyright notices contained within 110 | the Program. 111 | 112 | Each Contributor must identify itself as the originator of its Contribution, 113 | if any, in a manner that reasonably allows subsequent Recipients to identify 114 | the originator of the Contribution. 115 | 116 | 4. COMMERCIAL DISTRIBUTION 117 | Commercial distributors of software may accept certain responsibilities with 118 | respect to end users, business partners and the like. While this license is 119 | intended to facilitate the commercial use of the Program, the Contributor who 120 | includes the Program in a commercial product offering should do so in a manner 121 | which does not create potential liability for other Contributors. Therefore, 122 | if a Contributor includes the Program in a commercial product offering, such 123 | Contributor ("Commercial Contributor") hereby agrees to defend and indemnify 124 | every other Contributor ("Indemnified Contributor") against any losses, 125 | damages and costs (collectively "Losses") arising from claims, lawsuits and 126 | other legal actions brought by a third party against the Indemnified 127 | Contributor to the extent caused by the acts or omissions of such Commercial 128 | Contributor in connection with its distribution of the Program in a commercial 129 | product offering. The obligations in this section do not apply to any claims 130 | or Losses relating to any actual or alleged intellectual property 131 | infringement. In order to qualify, an Indemnified Contributor must: a) 132 | promptly notify the Commercial Contributor in writing of such claim, and b) 133 | allow the Commercial Contributor to control, and cooperate with the Commercial 134 | Contributor in, the defense and any related settlement negotiations. The 135 | Indemnified Contributor may participate in any such claim at its own expense. 136 | 137 | For example, a Contributor might include the Program in a commercial product 138 | offering, Product X. That Contributor is then a Commercial Contributor. If 139 | that Commercial Contributor then makes performance claims, or offers 140 | warranties related to Product X, those performance claims and warranties are 141 | such Commercial Contributor's responsibility alone. Under this section, the 142 | Commercial Contributor would have to defend claims against the other 143 | Contributors related to those performance claims and warranties, and if a 144 | court requires any other Contributor to pay any damages as a result, the 145 | Commercial Contributor must pay those damages. 146 | 147 | 5. NO WARRANTY 148 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN 149 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR 150 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, 151 | NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each 152 | Recipient is solely responsible for determining the appropriateness of using 153 | and distributing the Program and assumes all risks associated with its 154 | exercise of rights under this Agreement , including but not limited to the 155 | risks and costs of program errors, compliance with applicable laws, damage to 156 | or loss of data, programs or equipment, and unavailability or interruption of 157 | operations. 158 | 159 | 6. DISCLAIMER OF LIABILITY 160 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY 161 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, 162 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION 163 | LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 164 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 165 | ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE 166 | EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY 167 | OF SUCH DAMAGES. 168 | 169 | 7. GENERAL 170 | 171 | If any provision of this Agreement is invalid or unenforceable under 172 | applicable law, it shall not affect the validity or enforceability of the 173 | remainder of the terms of this Agreement, and without further action by the 174 | parties hereto, such provision shall be reformed to the minimum extent 175 | necessary to make such provision valid and enforceable. 176 | 177 | If Recipient institutes patent litigation against any entity (including a 178 | cross-claim or counterclaim in a lawsuit) alleging that the Program itself 179 | (excluding combinations of the Program with other software or hardware) 180 | infringes such Recipient's patent(s), then such Recipient's rights granted 181 | under Section 2(b) shall terminate as of the date such litigation is filed. 182 | 183 | All Recipient's rights under this Agreement shall terminate if it fails to 184 | comply with any of the material terms or conditions of this Agreement and does 185 | not cure such failure in a reasonable period of time after becoming aware of 186 | such noncompliance. If all Recipient's rights under this Agreement terminate, 187 | Recipient agrees to cease use and distribution of the Program as soon as 188 | reasonably practicable. However, Recipient's obligations under this Agreement 189 | and any licenses granted by Recipient relating to the Program shall continue 190 | and survive. 191 | 192 | Everyone is permitted to copy and distribute copies of this Agreement, but in 193 | order to avoid inconsistency the Agreement is copyrighted and may only be 194 | modified in the following manner. The Agreement Steward reserves the right to 195 | publish new versions (including revisions) of this Agreement from time to 196 | time. No one other than the Agreement Steward has the right to modify this 197 | Agreement. The Eclipse Foundation is the initial Agreement Steward. The 198 | Eclipse Foundation may assign the responsibility to serve as the Agreement 199 | Steward to a suitable separate entity. Each new version of the Agreement will 200 | be given a distinguishing version number. The Program (including 201 | Contributions) may always be distributed subject to the version of the 202 | Agreement under which it was received. In addition, after a new version of the 203 | Agreement is published, Contributor may elect to distribute the Program 204 | (including its Contributions) under the new version. Except as expressly 205 | stated in Sections 2(a) and 2(b) above, Recipient receives no rights or 206 | licenses to the intellectual property of any Contributor under this Agreement, 207 | whether expressly, by implication, estoppel or otherwise. All rights in the 208 | Program not expressly granted under this Agreement are reserved. 209 | 210 | This Agreement is governed by the laws of the State of New York and the 211 | intellectual property laws of the United States of America. No party to this 212 | Agreement will bring a legal action under this Agreement more than one year 213 | after the cause of action arose. Each party waives its rights to a jury trial 214 | in any resulting litigation. -------------------------------------------------------------------------------- /templates/js/admin.js: -------------------------------------------------------------------------------- 1 | /********************************************** 2 | 3 | *** SoluteDNS CE for WHMCS *** 4 | 5 | File: template/js/admin.js 6 | File version: 0.17.001 7 | Date: 09-11-2017 8 | 9 | Copyright (C) NetDistrict 2016-2017 10 | All Rights Reserved 11 | 12 | **********************************************/ 13 | 14 | function sendData(json) { 15 | 16 | NProgress.start(); 17 | 18 | if (typeof time1 !== 'undefined') { 19 | clearTimeout(time1); 20 | } 21 | if (typeof time2 !== 'undefined') { 22 | clearTimeout(time2); 23 | } 24 | 25 | $.ajax({ 26 | data: { 27 | 'data': json 28 | }, 29 | url: location.protocol + '//' + location.host + location.pathname + '?module=solutedns&action=post', 30 | method: "POST", 31 | success: function (data) { 32 | 33 | var result = JSON.parse(data); 34 | 35 | if (result) { 36 | result.forEach(function (data) { 37 | 38 | setMessage(data['title'], data['msg'], data['status'], data['tablereload'], data['pagereload'], data['fieldreset'], data['msgReset'], data['fixed'], data['errorFields']) 39 | 40 | if (data['syscheck'] == true) { 41 | syscheck(); 42 | } 43 | 44 | if (typeof data['field'] !== 'undefined') { 45 | setErrorField(data['field']); 46 | } 47 | 48 | }); 49 | } 50 | 51 | NProgress.done(); 52 | 53 | } 54 | }); 55 | 56 | } 57 | 58 | function setMessage(title, desc, status, tableReload, pageReload, fieldReset, msgReset, fixed, errorFields) { 59 | 60 | /* Message Reset */ 61 | if (msgReset == true) { 62 | resetMessages(); 63 | } 64 | 65 | /* Generate Unique ID */ 66 | var id = '4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { 67 | var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); 68 | return v.toString(16); 69 | }); 70 | 71 | /* Set message state */ 72 | if (status == 'error') { 73 | var state = 'alert2 alert2-danger'; 74 | } else { 75 | var state = 'alert2 alert2-' + status; 76 | } 77 | 78 | /* Add Message */ 79 | $('#msgConsole').append(''); 80 | 81 | /* Display Message */ 82 | $("#" + id).show("slow", function () { 83 | $('html, body').animate({ 84 | scrollTop: $("#msgConsole").offset().top - 10 85 | }, 400); 86 | if (fixed != true) { 87 | setTimeout(function () { 88 | $("#" + id).hide("slow"); 89 | }, 8000); 90 | setTimeout(function () { 91 | $("#" + id).remove(); 92 | }, 8500); 93 | } 94 | }); 95 | 96 | /* Reload Table Date */ 97 | if (tableReload == true) { 98 | SDNS_recordTable.fnReloadAjax(); 99 | } 100 | 101 | /* Reload Page */ 102 | if (pageReload == true) { 103 | setTimeout(function () { 104 | location.reload() 105 | }, 2000); 106 | } 107 | 108 | /* Reset Field Entries */ 109 | if (fieldReset == true) { 110 | clearFields(); 111 | resetDNSField(); 112 | } 113 | 114 | /* Set Error Field */ 115 | setErrorMark(errorFields); 116 | 117 | } 118 | 119 | function updateSettings(element) { 120 | var fields = $("#" + element + " :input").serializeArray(); 121 | var data = JSON.stringify(fields); 122 | 123 | sendData(data) 124 | } 125 | 126 | function resetMessages() { 127 | 128 | /* Clear Message Box */ 129 | $("#msgConsole").empty(); 130 | 131 | /* Retrieve System Messages */ 132 | getState(); 133 | 134 | if ($("#sdns_zone").val().length > 0) { 135 | getZoneState(); 136 | } 137 | 138 | } 139 | 140 | function getState() { 141 | 142 | var zone = $("#sdns_zone").val(); 143 | 144 | var item = { 145 | action: 'systemState', 146 | zone: zone, 147 | }; 148 | 149 | jsonString = JSON.stringify(item); 150 | 151 | sendData(jsonString); 152 | 153 | } 154 | 155 | function setErrorMark(fields) { 156 | 157 | /* Remove previous set errors */ 158 | $(".col-md-1 div").removeClass("has-error"); 159 | $(".col-md-2 div").removeClass("has-error"); 160 | $(".col-md-3 div").removeClass("has-error"); 161 | $(".col-md-9 div").removeClass("has-error"); 162 | 163 | if (fields != null) { 164 | 165 | /* Add error to selected fields */ 166 | var fields = fields.split(","); 167 | 168 | fields.forEach(function (field) { 169 | $("#" + field + "_field").addClass("has-error"); 170 | ; 171 | }); 172 | 173 | } 174 | 175 | } 176 | 177 | function isDataTable(nTable) { 178 | return $.fn.DataTable.fnIsDataTable('#' + nTable); 179 | } 180 | 181 | function selectTemplate(value) { 182 | SDNS_recordTable.fnDestroy(); 183 | drawRecords('sdns_template_records', value); 184 | } 185 | 186 | function edit(id) { 187 | 188 | /* Reset previous selected field */ 189 | resetDNSField(); 190 | 191 | /* Enable current fields */ 192 | $('#sdns_name_' + id).prop('disabled', false); 193 | $('#sdns_type_' + id).prop('disabled', false); 194 | $('#sdns_content_' + id).prop('disabled', false); 195 | $('#sdns_prio_' + id).prop('disabled', false); 196 | $('#sdns_ttl_' + id).prop('disabled', false); 197 | 198 | $('#sdns_edit_' + id).hide(); 199 | $('#sdns_save_' + id).show(); 200 | 201 | setRecord(id); 202 | 203 | } 204 | 205 | function resetDNSField() { 206 | 207 | var id = $("#sdns_record").val(); 208 | 209 | if (id > 1) { 210 | /* Disable previous fields */ 211 | $('#sdns_name_' + id).prop('disabled', true); 212 | $('#sdns_type_' + id).prop('disabled', true); 213 | $('#sdns_content_' + id).prop('disabled', true); 214 | $('#sdns_prio_' + id).prop('disabled', true); 215 | $('#sdns_ttl_' + id).prop('disabled', true); 216 | 217 | $('#sdns_save_' + id).hide(); 218 | $('#sdns_edit_' + id).show(); 219 | } 220 | } 221 | 222 | function deleteSelected() { 223 | 224 | var deleteArray = []; 225 | 226 | $("input:checkbox[name=sdns_select]:checked").each(function () { 227 | deleteArray.push($(this).val()); 228 | }); 229 | 230 | var zone = $("#sdns_zone").val(); 231 | var records = deleteArray; 232 | 233 | var data = { 234 | action: 'deleteselectedrecords', 235 | zone: zone, 236 | records: records 237 | }; 238 | 239 | jsonString = JSON.stringify(data); 240 | 241 | sendData(jsonString); 242 | 243 | } 244 | 245 | function setErrorField(field) { 246 | var record_id = $("#sdns_record").val(); 247 | 248 | $("#sdns_z-" + field + "_" + record_id).addClass("has-error"); 249 | } 250 | 251 | function clearFields() { 252 | $("#sdns_name_0").val(""); 253 | $("#sdns_content_0").val(""); 254 | $("#sdns_prio_0").val(""); 255 | } 256 | 257 | function clearErrorField() { 258 | 259 | var record_id = $("#sdns_record").val(); 260 | 261 | $("#sdns_z-name_" + record_id).removeClass("has-error"); 262 | $("#sdns_z-type_" + record_id).removeClass("has-error"); 263 | $("#sdns_z-content_" + record_id).removeClass("has-error"); 264 | $("#sdns_z-prio_" + record_id).removeClass("has-error"); 265 | $("#sdns_z-ttl_" + record_id).removeClass("has-error"); 266 | 267 | } 268 | 269 | function record_add(type) { 270 | 271 | setRecord(0); 272 | clearErrorField() 273 | 274 | var zone = $("#sdns_zone").val(); 275 | 276 | var var1 = $("#sdns_name_0").val(); 277 | var var2 = $("#sdns_type_0").val(); 278 | var var3 = $("#sdns_content_0").val(); 279 | var var4 = $("#sdns_prio_0").val(); 280 | var var5 = $("#sdns_ttl_0").val(); 281 | 282 | if (type == 'template') { 283 | zone = $("#sdns_template").val(); 284 | var item = { 285 | action: 'addtemplate', 286 | zone: zone, 287 | name: var1, 288 | type: var2, 289 | content: var3, 290 | prio: var4, 291 | ttl: var5, 292 | }; 293 | } else { 294 | var item = { 295 | action: 'addrecord', 296 | zone: zone, 297 | name: var1, 298 | type: var2, 299 | content: var3, 300 | prio: var4, 301 | ttl: var5 302 | }; 303 | } 304 | 305 | jsonString = JSON.stringify(item); 306 | 307 | sendData(jsonString); 308 | 309 | } 310 | 311 | function record_edit(type, record_id) { 312 | 313 | clearErrorField() 314 | 315 | var zone = $("#sdns_zone").val(); 316 | 317 | var var1 = $("#sdns_name_" + record_id).val(); 318 | var var2 = $("#sdns_type_" + record_id).val(); 319 | var var3 = $("#sdns_content_" + record_id).val(); 320 | var var4 = $("#sdns_prio_" + record_id).val(); 321 | var var5 = $("#sdns_ttl_" + record_id).val(); 322 | 323 | if (type == 'template') { 324 | var item = { 325 | action: 'edittemplate', 326 | zone: zone, 327 | record_id: record_id, 328 | name: var1, 329 | type: var2, 330 | content: var3, 331 | prio: var4, 332 | ttl: var5, 333 | }; 334 | } else { 335 | var item = { 336 | action: 'editrecord', 337 | zone: zone, 338 | record_id: record_id, 339 | name: var1, 340 | type: var2, 341 | content: var3, 342 | prio: var4, 343 | ttl: var5 344 | }; 345 | } 346 | 347 | jsonString = JSON.stringify(item); 348 | 349 | sendData(jsonString); 350 | 351 | } 352 | 353 | function record_delete(type) { 354 | 355 | var zone = $("#sdns_zone").val(); 356 | var record_id = $("#sdns_record").val(); 357 | 358 | if (type == 'template') { 359 | var item = { 360 | action: 'deletetemplate', 361 | record_id: record_id, 362 | }; 363 | } else { 364 | var item = { 365 | action: 'deleterecord', 366 | zone: zone, 367 | record_id: record_id 368 | }; 369 | } 370 | 371 | jsonString = JSON.stringify(item); 372 | 373 | sendData(jsonString); 374 | 375 | } 376 | 377 | function ExportZone(zone) { 378 | 379 | var item = { 380 | action: 'export', 381 | zone: zone 382 | }; 383 | 384 | jsonString = JSON.stringify(item); 385 | 386 | $.ajax({ 387 | data: { 388 | 'data': jsonString 389 | }, 390 | url: location.protocol + '//' + location.host + location.pathname + '?module=solutedns&action=post', 391 | method: "GET", 392 | success: function (data) { 393 | jQuery("textarea#textarea_export").val(data); 394 | }, 395 | error: function (data) { 396 | jQuery("textarea#textarea_export").val('Error could not load zone.'); 397 | } 398 | }); 399 | } 400 | 401 | function importZone() { 402 | 403 | var action = 'import'; 404 | var zone = $("#sdns_zone").val(); 405 | var zonedata = $("#textarea_import").val(); 406 | var overwrite = $('#overwrite').is(":checked"); 407 | 408 | var item = { 409 | action: action, 410 | zone: zone, 411 | bind: zonedata, 412 | overwrite: overwrite 413 | }; 414 | 415 | jsonString = JSON.stringify(item); 416 | 417 | sendData(jsonString); 418 | 419 | } 420 | 421 | function applyTemplate() { 422 | 423 | var action = 'applytemplate'; 424 | var zone = $("#sdns_zone").val(); 425 | var template = $("#sdns_apply_template").val(); 426 | 427 | var item = { 428 | action: action, 429 | zone: zone, 430 | template: template 431 | }; 432 | 433 | jsonString = JSON.stringify(item); 434 | 435 | sendData(jsonString); 436 | 437 | } 438 | 439 | function zone_delete(id) { 440 | 441 | var action = 'deletezone'; 442 | var zone = id; 443 | 444 | var item = { 445 | action: action, 446 | zone: zone 447 | }; 448 | 449 | jsonString = JSON.stringify(item); 450 | 451 | sendData(jsonString); 452 | 453 | } 454 | 455 | function dnssec(action, zone, key) { 456 | 457 | var item = { 458 | action: 'dnssec_' + action, 459 | zone: zone, 460 | key: key 461 | }; 462 | 463 | jsonString = JSON.stringify(item); 464 | 465 | sendData(jsonString); 466 | 467 | } 468 | 469 | function dnssec_addkey(zone) { 470 | 471 | var flag = $('#sdns_dnssec_flag').val(); 472 | var bits = $('#sdns_dnssec_bits').val(); 473 | var algorithm = $('#sdns_dnssec_algorithm').val(); 474 | 475 | var item = { 476 | action: 'dnssec_addkey', 477 | zone: zone, 478 | flag: flag, 479 | bits: bits, 480 | algorithm: algorithm 481 | }; 482 | 483 | jsonString = JSON.stringify(item); 484 | 485 | sendData(jsonString); 486 | 487 | } 488 | 489 | function setRecord(id) { 490 | $("#sdns_record").val(id); 491 | } 492 | 493 | function setZone(id) { 494 | $("#sdns_zone").val(id); 495 | } 496 | 497 | function setTemplate(id) { 498 | $("#sdns_template_id").val(id); 499 | } 500 | 501 | function syscheck(type) { 502 | 503 | var item = { 504 | action: 'systemCheck', 505 | type: type, 506 | }; 507 | 508 | jsonString = JSON.stringify(item); 509 | 510 | sendData(jsonString); 511 | 512 | } 513 | 514 | $(document).ready(function () { 515 | 516 | /* Javascript to enable link to tab */ 517 | var url = document.location.toString(); 518 | if (url.match('#')) { 519 | $('.nav-tabs a[href="#' + url.split('#')[1] + '"]').tab('show'); 520 | var atab = url.split('#')[1]; 521 | } 522 | 523 | /* HTML5 Prevent scrolling! */ 524 | $('.nav-tabs a').on('shown.bs.tab', function (e) { 525 | if (history.pushState) { 526 | history.pushState(null, null, e.target.hash); 527 | } else { 528 | window.location.hash = e.target.hash; //Polyfill for old browsers 529 | } 530 | }) 531 | 532 | /* Activate tooltip */ 533 | $(function () { 534 | $('[data-toggle="tooltip"]').tooltip({container: 'body', html: true, 535 | 536 | template: '' 537 | 538 | }) 539 | }) 540 | 541 | }); -------------------------------------------------------------------------------- /templates/admin_nameserver.tpl: -------------------------------------------------------------------------------- 1 |

{$LANG.admin_menu_nameserver}

2 | 3 | 7 | 8 |
9 |
10 | 11 | 12 |

{$LANG.admin_nameserver_database_details}

13 | 14 |
15 |
16 | 17 |
18 |
19 |
20 | 21 |
22 |
23 |
24 | 25 |
26 |
27 |
28 |
29 | 30 |
31 |
32 |
33 | 34 |
35 |
36 |
37 | 38 |
39 |
40 |
41 |
42 | 43 |
44 |
45 |
46 | 47 |
48 |
49 |
50 |
51 |
52 |
53 | 54 |
55 |
56 |
57 | 58 |
59 |
60 |
61 |
62 |
63 |
64 | 65 |
66 |
67 |
68 | 69 |
70 |
71 |
72 |
73 |
74 |
75 | 76 |
77 |
78 |
79 | 83 |
84 |
85 |
86 | 87 |
88 |
89 | 90 |
91 |

{$LANG.admin_nameserver_nameservers}

92 | 93 |
94 |
95 | 96 |
97 |
98 |
99 | 100 |
101 |
102 |
103 | 104 |
105 |
106 |
107 |
108 | 109 |
110 |
111 |
112 | 113 |
114 |
115 |
116 |
117 |
118 |
119 | 120 |
121 |
122 |
123 | 124 |
125 |
126 |
127 |
128 |
129 |
130 | 131 |
132 |
133 |
134 | 135 |
136 |
137 |
138 |
139 |
140 |
141 | 142 |
143 |
144 |
145 | 146 |
147 |
148 |
149 |
150 |
151 |
152 | 153 |
154 |
155 |
156 | 157 |
158 |
159 |
160 | 161 |
162 |
163 | 164 |
165 |

{$LANG.admin_nameserver_ssh_details}

166 | 167 |
168 |
169 | 170 |
171 |
172 |
173 | 174 |
175 |
176 |
177 | 178 |
179 |
180 |
181 |
182 | 183 |
184 |
185 |
186 | 187 |
188 |
189 |
190 | 191 |
192 |
193 |
194 |
195 | 196 |
197 |
198 |
199 | 200 |
201 |
202 |
203 |
204 |
205 |
206 | 207 |
208 |
209 |
210 | 211 |
212 |
213 |
214 |
215 |
216 |
217 | 218 |
219 |
220 |
221 | 222 | 223 |
224 |
225 |
226 | 227 |
228 |

{$LANG.admin_nameserver_dnssec_options}

229 | 230 |
231 |
232 | 233 |
234 |
235 |
236 | 240 |
241 |
242 |
243 | 244 |
245 |
246 | 247 |
248 |
249 |
250 | 251 | 252 |
253 |
254 |
255 |
256 |
257 | 258 |
259 |
260 |
261 | 262 | 263 |
264 |
265 |
266 |
267 |
268 | 269 |
270 |
271 |
272 | 273 | 274 |
275 |
276 |
277 |
278 |
279 | 280 |
281 |
282 |
283 | 284 | 285 |
286 |
287 |
288 |
289 |
290 | 291 |
292 |
293 |
294 | 295 | 296 |
297 |
298 |
299 | 300 |
301 |
302 | 303 |
304 |
305 |
-------------------------------------------------------------------------------- /templates/admin_manage_records.tpl: -------------------------------------------------------------------------------- 1 | {assign var=records value=Controller::inConfig(record_types)} 2 | 3 |
4 |
5 |

{$LANG.admin_manage_title_zone}: 6 |
{$domain->domain} {if $dnssec.nsec} {$dnssec.nsec}{/if}

7 |
8 | 29 |
30 | 31 |

{$LANG.admin_menu_records}

32 | 33 |
34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 |
{$LANG.global_dns_id}{$LANG.global_dns_name}{$LANG.global_dns_type}{$LANG.global_dns_content}{$LANG.global_dns_prio}{$LANG.global_dns_ttl}
{$LANG.global_table_loading_data}
53 | 54 |
55 | 56 | 57 |
58 | 109 |
110 | 111 | 112 |
113 | 132 |
133 | 134 | 135 |
136 | 172 |
173 | 174 | 175 |
176 | 208 |
209 | 210 | 211 |
212 | 232 |
233 | 234 | 235 |
236 | 256 |
257 | 258 | 259 | 260 | -------------------------------------------------------------------------------- /solutedns.php: -------------------------------------------------------------------------------- 1 | 26 | * @copyright NetDistrict 27 | * @link https://www.solutedns.com 28 | * */ 29 | if (!defined("WHMCS")) { 30 | die("This file cannot be accessed directly"); 31 | } 32 | 33 | /** 34 | * Required libraries and dependencies 35 | */ 36 | use WHMCS\Module\Addon\SoluteDNS\Admin\AdminDispatcher; 37 | use WHMCS\Module\Addon\SoluteDNS\Client\ClientDispatcher; 38 | use Illuminate\Database\Capsule\Manager as Capsule; 39 | 40 | /** 41 | * Module Configueation. 42 | * 43 | * @return array 44 | */ 45 | function solutedns_config() { 46 | return [ 47 | 'name' => 'SoluteDNS - Community Edition', 48 | 'description' => 'DNS Management for PowerDNS nameservers with MySQL back-end.', 49 | 'author' => 'NetDistrict', 50 | 'language' => 'english', 51 | 'version' => '1.19.001', 52 | ]; 53 | } 54 | 55 | /** 56 | * Activate. 57 | * 58 | * Called upon activation of the module for the first time. 59 | * 60 | * @return array Optional success/failure message 61 | */ 62 | function solutedns_activate() { 63 | 64 | // Database 65 | $pdo = Capsule::connection()->getPdo(); 66 | $pdo->beginTransaction(); 67 | 68 | try { 69 | 70 | // Create Table: SETTINGS 71 | $query = "CREATE TABLE IF NOT EXISTS `mod_solutedns_settings` ( 72 | `id` int(5) NOT NULL AUTO_INCREMENT, 73 | `setting` varchar(32) NOT NULL, 74 | `value` text NOT NULL, 75 | PRIMARY KEY (`id`), 76 | UNIQUE KEY `id` (`id`) 77 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ;"; 78 | 79 | $pdo->exec($query); 80 | 81 | $query = "ALTER TABLE mod_solutedns_settings ADD UNIQUE KEY (`setting`);"; 82 | $pdo->exec($query); 83 | 84 | // Add Settings 85 | 86 | $query = "INSERT IGNORE INTO mod_solutedns_settings (setting,value) VALUES ('eula', '');"; 87 | $pdo->exec($query); 88 | 89 | $query = "INSERT IGNORE INTO mod_solutedns_settings (setting,value) VALUES ('record_types', 'A,AAAA,CAA,CNAME,MX,NS,SRV,TXT,SSHFP,TLSA');"; 90 | $pdo->exec($query); 91 | 92 | $query = "INSERT IGNORE INTO mod_solutedns_settings (setting,value) VALUES ('soa_hostmaster', 'hostmaster.{domain}');"; 93 | $pdo->exec($query); 94 | 95 | $query = "INSERT IGNORE INTO mod_solutedns_settings (setting,value) VALUES ('soa_serial', 'default');"; 96 | $pdo->exec($query); 97 | 98 | $query = "INSERT IGNORE INTO mod_solutedns_settings (setting,value) VALUES ('soa_refresh', '3600');"; 99 | $pdo->exec($query); 100 | 101 | $query = "INSERT IGNORE INTO mod_solutedns_settings (setting,value) VALUES ('soa_retry', '600');"; 102 | $pdo->exec($query); 103 | 104 | $query = "INSERT IGNORE INTO mod_solutedns_settings (setting,value) VALUES ('soa_expire', '604800');"; 105 | $pdo->exec($query); 106 | 107 | $query = "INSERT IGNORE INTO mod_solutedns_settings (setting,value) VALUES ('soa_ttl', '3600');"; 108 | $pdo->exec($query); 109 | 110 | $query = "INSERT IGNORE INTO mod_solutedns_settings (setting,value) VALUES ('custom_primary', '');"; 111 | $pdo->exec($query); 112 | 113 | $query = "INSERT IGNORE INTO mod_solutedns_settings (setting,value) VALUES ('record_limit', '0');"; 114 | $pdo->exec($query); 115 | 116 | $query = "INSERT IGNORE INTO mod_solutedns_settings (setting,value) VALUES ('respect_registrar', 'on');"; 117 | $pdo->exec($query); 118 | 119 | $query = "INSERT IGNORE INTO mod_solutedns_settings (setting,value) VALUES ('force_dns', '');"; 120 | $pdo->exec($query); 121 | 122 | $query = "INSERT IGNORE INTO mod_solutedns_settings (setting,value) VALUES ('hide_soa', 'on');"; 123 | $pdo->exec($query); 124 | 125 | $query = "INSERT IGNORE INTO mod_solutedns_settings (setting,value) VALUES ('disable_ns', 'on');"; 126 | $pdo->exec($query); 127 | 128 | $query = "INSERT IGNORE INTO mod_solutedns_settings (setting,value) VALUES ('preset_ttl', 'on');"; 129 | $pdo->exec($query); 130 | 131 | $query = "INSERT IGNORE INTO mod_solutedns_settings (setting,value) VALUES ('dns_pagination', '');"; 132 | $pdo->exec($query); 133 | 134 | $query = "INSERT IGNORE INTO mod_solutedns_settings (setting,value) VALUES ('client_urlrewrite', '');"; 135 | $pdo->exec($query); 136 | 137 | $query = "INSERT IGNORE INTO mod_solutedns_settings (setting,value) VALUES ('auto_create', '');"; 138 | $pdo->exec($query); 139 | 140 | $query = "INSERT IGNORE INTO mod_solutedns_settings (setting,value) VALUES ('auto_delete', '');"; 141 | $pdo->exec($query); 142 | 143 | $query = "INSERT IGNORE INTO mod_solutedns_settings (setting,value) VALUES ('auto_delete_whmcs', '');"; 144 | $pdo->exec($query); 145 | 146 | $query = "INSERT IGNORE INTO mod_solutedns_settings (setting,value) VALUES ('auto_enabled', 'on');"; 147 | $pdo->exec($query); 148 | 149 | $query = "INSERT IGNORE INTO mod_solutedns_settings (setting,value) VALUES ('auto_todo', '');"; 150 | $pdo->exec($query); 151 | 152 | $query = "INSERT IGNORE INTO mod_solutedns_settings (setting,value) VALUES ('maintenance', '');"; 153 | $pdo->exec($query); 154 | 155 | $query = "INSERT IGNORE INTO mod_solutedns_settings (setting,value) VALUES ('logging', 'on');"; 156 | $pdo->exec($query); 157 | 158 | $query = "INSERT IGNORE INTO mod_solutedns_settings (setting,value) VALUES ('last_cron', '');"; 159 | $pdo->exec($query); 160 | 161 | $query = "INSERT IGNORE INTO mod_solutedns_settings (setting,value) VALUES ('license', '');"; 162 | $pdo->exec($query); 163 | 164 | // Create Table: NAMESERVERS 165 | $query = "CREATE TABLE IF NOT EXISTS `mod_solutedns_nameservers` ( 166 | `id` int(5) NOT NULL AUTO_INCREMENT, 167 | `db_host` varchar(254) NOT NULL, 168 | `db_port` int(5) NOT NULL, 169 | `db_user` text NOT NULL, 170 | `db_pass` text NOT NULL, 171 | `db_name` text NOT NULL, 172 | `zone_type` varchar(6) NOT NULL, 173 | `ns0` varchar(254) NOT NULL, 174 | `ns1` varchar(254) NOT NULL, 175 | `ns2` varchar(254) NOT NULL, 176 | `ns3` varchar(254) NOT NULL, 177 | `ns4` varchar(254) NOT NULL, 178 | `ns5` varchar(254) NOT NULL, 179 | `ssh_host` varchar(254) NOT NULL, 180 | `ssh_port` int(5) NOT NULL, 181 | `ssh_user` text NOT NULL, 182 | `ssh_pass` text NOT NULL, 183 | `ssh_key` text NOT NULL, 184 | `dnssec_enable` varchar(5) NOT NULL, 185 | `dnssec_rectify` varchar(5) NOT NULL, 186 | `dnssec_auto` varchar(5) NOT NULL, 187 | `dnssec_nsec3` varchar(5) NOT NULL, 188 | `dnssec_client` varchar(5) NOT NULL, 189 | `version` int(2) NOT NULL, 190 | PRIMARY KEY (`id`), 191 | UNIQUE KEY `id` (`id`) 192 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;"; 193 | 194 | $pdo->exec($query); 195 | 196 | // Add nameserver 197 | $query = "INSERT INTO `mod_solutedns_nameservers` (dnssec_rectify, version) VALUES ('on', '3');"; 198 | $pdo->exec($query); 199 | 200 | // Create Table: TEMPLATE RECORDS 201 | 202 | $query = "CREATE TABLE IF NOT EXISTS `mod_solutedns_template_records` ( 203 | `id` int(10) NOT NULL AUTO_INCREMENT, 204 | `product_id` int(10) NOT NULL, 205 | `name` varchar(255) NOT NULL, 206 | `type` varchar(6) NOT NULL, 207 | `content` varchar(255) NOT NULL, 208 | `ttl` int(10) NOT NULL, 209 | `prio` int(10) NOT NULL, 210 | PRIMARY KEY (`id`), 211 | UNIQUE KEY `id` (`id`) 212 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;"; 213 | 214 | $pdo->exec($query); 215 | 216 | // Add Example Record 217 | $query = "INSERT IGNORE INTO mod_solutedns_template_records (product_id, name, type, content, ttl, prio) VALUES ('0', '{domain}', 'TXT', '\"Example Record\"', '3600', '0')"; 218 | $pdo->exec($query); 219 | 220 | // Create Table: CRON QUEUE 221 | $query = "CREATE TABLE IF NOT EXISTS `mod_solutedns_cron_queue` ( 222 | `id` int(10) NOT NULL AUTO_INCREMENT, 223 | `domain_id` int(10) NOT NULL, 224 | `name` text NOT NULL, 225 | `action` text NOT NULL, 226 | `time` int(10) NOT NULL, 227 | PRIMARY KEY (`id`), 228 | UNIQUE KEY `id` (`id`) 229 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;"; 230 | 231 | $pdo->exec($query); 232 | 233 | // Create Table: CACHE 234 | $query = "CREATE TABLE IF NOT EXISTS `mod_solutedns_cache` ( 235 | `id` int(11) NOT NULL AUTO_INCREMENT, 236 | `domain_id` int(11) NOT NULL, 237 | `type` varchar(32) NOT NULL, 238 | `content` text NOT NULL, 239 | `time` varchar(11) NOT NULL, 240 | PRIMARY KEY (`id`), 241 | UNIQUE KEY `id` (`id`) 242 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1;"; 243 | 244 | $pdo->exec($query); 245 | 246 | // Complete Activation 247 | $pdo->commit(); 248 | 249 | return [ 250 | 'status' => 'success', // Supported values here include: success, error or info 251 | 'description' => 'SoluteDNS for WHMCS - Community Edition has been activated successfully.', 252 | ]; 253 | } catch (Exception $e) { 254 | 255 | $pdo->rollBack(); 256 | 257 | return [ 258 | 'status' => 'error', // Supported values here include: success, error or info 259 | 'description' => 'An database error occured during activation: ' . $e->getMessage(), 260 | ]; 261 | } 262 | } 263 | 264 | /** 265 | * Deactivate. 266 | * 267 | * Called upon deactivation of the module. 268 | * 269 | * @return array Optional success/failure message 270 | */ 271 | function solutedns_deactivate() { 272 | 273 | // Database 274 | $pdo = Capsule::connection()->getPdo(); 275 | $pdo->beginTransaction(); 276 | 277 | try { 278 | 279 | // Remove Database Tables 280 | $query = "DROP TABLE IF EXISTS `mod_solutedns_cache`;"; 281 | $pdo->exec($query); 282 | 283 | $query = "DROP TABLE IF EXISTS `mod_solutedns_cron_queue`;"; 284 | $pdo->exec($query); 285 | 286 | $query = "DROP TABLE IF EXISTS `mod_solutedns_nameservers`;"; 287 | $pdo->exec($query); 288 | 289 | $query = "DROP TABLE IF EXISTS `mod_solutedns_settings`;"; 290 | $pdo->exec($query); 291 | 292 | $query = "DROP TABLE IF EXISTS `mod_solutedns_template_records`;"; 293 | $pdo->exec($query); 294 | 295 | // Complete Deactivation 296 | $pdo->commit(); 297 | 298 | return [ 299 | 'status' => 'success', // Supported values here include: success, error or info 300 | 'description' => 'SoluteDNS for WHMCS - Community Edition has been deactivated successfully.', 301 | ]; 302 | } catch (Exception $e) { 303 | 304 | $pdo->rollBack(); 305 | 306 | return [ 307 | 'status' => 'error', // Supported values here include: success, error or info 308 | 'description' => 'An database error occured during deactivation: ' . $e->getMessage(), 309 | ]; 310 | } 311 | } 312 | 313 | /** 314 | * Upgrade. 315 | * 316 | * Called the first time the module is accessed following an update. 317 | * 318 | * @return void 319 | */ 320 | function solutedns_upgrade($vars) { 321 | $currentlyVersion = $vars['version']; 322 | 323 | if (version_compare($currentlyVersion, '0.18.001', '<')) { 324 | // No db changes 325 | $newVersion = 'v0.18.001'; 326 | } 327 | 328 | if (version_compare($currentlyVersion, '1.19.001', '<')) { 329 | 330 | // Database 331 | $pdo = Capsule::connection()->getPdo(); 332 | $pdo->beginTransaction(); 333 | 334 | try { 335 | 336 | $query = "ALTER TABLE mod_solutedns_nameservers MODIFY db_host varchar(254);"; 337 | $pdo->exec($query); 338 | $query = "ALTER TABLE mod_solutedns_nameservers MODIFY db_user text;"; 339 | $pdo->exec($query); 340 | $query = "ALTER TABLE mod_solutedns_nameservers MODIFY db_name text;"; 341 | $pdo->exec($query); 342 | $query = "ALTER TABLE mod_solutedns_nameservers MODIFY zone_type varchar(6);"; 343 | $pdo->exec($query); 344 | $query = "ALTER TABLE mod_solutedns_nameservers MODIFY ns0 varchar(254);"; 345 | $pdo->exec($query); 346 | $query = "ALTER TABLE mod_solutedns_nameservers MODIFY ns1 varchar(254);"; 347 | $pdo->exec($query); 348 | $query = "ALTER TABLE mod_solutedns_nameservers MODIFY ns2 varchar(254);"; 349 | $pdo->exec($query); 350 | $query = "ALTER TABLE mod_solutedns_nameservers MODIFY ns3 varchar(254);"; 351 | $pdo->exec($query); 352 | $query = "ALTER TABLE mod_solutedns_nameservers MODIFY ns4 varchar(254);"; 353 | $pdo->exec($query); 354 | $query = "ALTER TABLE mod_solutedns_nameservers MODIFY ns5 varchar(254);"; 355 | $pdo->exec($query); 356 | $query = "ALTER TABLE mod_solutedns_nameservers MODIFY ssh_host varchar(254);"; 357 | $pdo->exec($query); 358 | $query = "ALTER TABLE mod_solutedns_nameservers MODIFY ssh_user text;"; 359 | $pdo->exec($query); 360 | $query = "INSERT IGNORE INTO mod_solutedns_settings (setting,value) VALUES ('force_dns', '');"; 361 | $pdo->exec($query); 362 | 363 | $pdo->commit(); 364 | 365 | } catch (Exception $e) { 366 | 367 | $pdo->rollBack(); 368 | $error = true; 369 | 370 | logActivity('SoluteDNS upgrade '.$newVersion .' failed: '. $e->getMessage()); 371 | 372 | } 373 | 374 | $newVersion = 'v1.19.001'; 375 | } 376 | 377 | ### 378 | 379 | if ($error != true) { 380 | logActivity('SoluteDNS has been upgraded to: '. $newVersion .'.'); 381 | } 382 | } 383 | 384 | /** 385 | * Admin Area Output. 386 | * 387 | * Called when the addon module is accessed via the admin area. 388 | * 389 | * @see solutedns\Admin\Controller@index 390 | * 391 | * @return string 392 | */ 393 | function solutedns_output($vars) { 394 | // Get common module parameters 395 | $modulelink = $vars['modulelink']; 396 | $version = $vars['version']; 397 | $_lang = $vars['_lang']; 398 | $vars['base_path'] = dirname(__FILE__); 399 | 400 | // Dispatch and handle request here. What follows is a demonstration of one 401 | // possible way of handling this using a very basic dispatcher implementation. 402 | 403 | $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : ''; 404 | 405 | $dispatcher = new AdminDispatcher(); 406 | $response = $dispatcher->dispatch($action, $vars); 407 | echo $response; 408 | } 409 | 410 | /** 411 | * Admin Area Sidebar Output. 412 | * 413 | * Used to render output in the admin area sidebar. 414 | * 415 | * @param array $vars 416 | * 417 | * @return string 418 | */ 419 | function solutedns_sidebar($vars) { 420 | // Get common module parameters 421 | $modulelink = $vars['modulelink']; 422 | $version = $vars['version']; 423 | $_lang = $vars['_lang']; 424 | 425 | $sidebar = ' 429 | '; 440 | 441 | return $sidebar; 442 | } 443 | 444 | /** 445 | * Client Area Output. 446 | * 447 | * Called when the addon module is accessed via the client area. 448 | * 449 | * @see solutedns\Client\Controller@index 450 | * 451 | * @return array 452 | */ 453 | function solutedns_clientarea($vars) { 454 | // Get common module parameters 455 | $modulelink = $vars['modulelink']; 456 | $version = $vars['version']; 457 | $_lang = $vars['_lang']; 458 | 459 | // Dispatch and handle request here. What follows is a demonstration of one 460 | // possible way of handling this using a very basic dispatcher implementation. 461 | 462 | $action = isset($_REQUEST['action']) ? $_REQUEST['action'] : ''; 463 | 464 | $dispatcher = new ClientDispatcher(); 465 | return $dispatcher->dispatch($action, $vars); 466 | } 467 | -------------------------------------------------------------------------------- /lib/Vendor/DataTables/SSP.php: -------------------------------------------------------------------------------- 1 | isset ( $request['draw'] ) ? 269 | intval( $request['draw'] ) : 270 | 0, 271 | "recordsTotal" => intval( $recordsTotal ), 272 | "recordsFiltered" => intval( $recordsFiltered ), 273 | "data" => self::data_output( $columns, $data ) 274 | ); 275 | } 276 | 277 | 278 | /** 279 | * The difference between this method and the `simple` one, is that you can 280 | * apply additional `where` conditions to the SQL queries. These can be in 281 | * one of two forms: 282 | * 283 | * * 'Result condition' - This is applied to the result set, but not the 284 | * overall paging information query - i.e. it will not effect the number 285 | * of records that a user sees they can have access to. This should be 286 | * used when you want apply a filtering condition that the user has sent. 287 | * * 'All condition' - This is applied to all queries that are made and 288 | * reduces the number of records that the user can access. This should be 289 | * used in conditions where you don't want the user to ever have access to 290 | * particular records (for example, restricting by a login id). 291 | * 292 | * @param array $request Data sent to server by DataTables 293 | * @param array|PDO $conn PDO connection resource or connection parameters array 294 | * @param string $table SQL table to query 295 | * @param string $primaryKey Primary key of the table 296 | * @param array $columns Column information array 297 | * @param string $whereResult WHERE condition to apply to the result set 298 | * @param string $whereAll WHERE condition to apply to all queries 299 | * @return array Server-side processing response array 300 | */ 301 | static function complex ( $request, $conn, $table, $primaryKey, $columns, $whereResult=null, $whereAll=null ) 302 | { 303 | $bindings = array(); 304 | $db = self::db( $conn ); 305 | $localWhereResult = array(); 306 | $localWhereAll = array(); 307 | $whereAllSql = ''; 308 | 309 | // Build the SQL query string from the request 310 | $limit = self::limit( $request, $columns ); 311 | $order = self::order( $request, $columns ); 312 | $where = self::filter( $request, $columns, $bindings ); 313 | 314 | $whereResult = self::_flatten( $whereResult ); 315 | $whereAll = self::_flatten( $whereAll ); 316 | 317 | if ( $whereResult ) { 318 | $where = $where ? 319 | $where .' AND '.$whereResult : 320 | 'WHERE '.$whereResult; 321 | } 322 | 323 | if ( $whereAll ) { 324 | $where = $where ? 325 | $where .' AND '.$whereAll : 326 | 'WHERE '.$whereAll; 327 | 328 | $whereAllSql = 'WHERE '.$whereAll; 329 | } 330 | 331 | // Main query to actually get the data 332 | $data = self::sql_exec( $db, $bindings, 333 | "SELECT `".implode("`, `", self::pluck($columns, 'db'))."` 334 | FROM `$table` 335 | $where 336 | $order 337 | $limit" 338 | ); 339 | 340 | // Data set length after filtering 341 | $resFilterLength = self::sql_exec( $db, $bindings, 342 | "SELECT COUNT(`{$primaryKey}`) 343 | FROM `$table` 344 | $where" 345 | ); 346 | $recordsFiltered = $resFilterLength[0][0]; 347 | 348 | // Total data set length 349 | $resTotalLength = self::sql_exec( $db, $bindings, 350 | "SELECT COUNT(`{$primaryKey}`) 351 | FROM `$table` ". 352 | $where 353 | ); 354 | $recordsTotal = $resTotalLength[0][0]; 355 | 356 | /* 357 | * Output 358 | */ 359 | return array( 360 | "draw" => isset ( $request['draw'] ) ? 361 | intval( $request['draw'] ) : 362 | 0, 363 | "recordsTotal" => intval( $recordsTotal ), 364 | "recordsFiltered" => intval( $recordsFiltered ), 365 | "data" => self::data_output( $columns, $data ) 366 | ); 367 | } 368 | 369 | 370 | /** 371 | * Connect to the database 372 | * 373 | * @param array $sql_details SQL server connection details array, with the 374 | * properties: 375 | * * host - host name 376 | * * db - database name 377 | * * user - user name 378 | * * pass - user password 379 | * @return resource Database connection handle 380 | */ 381 | static function sql_connect ( $sql_details ) 382 | { 383 | try { 384 | if ($sql_details['type'] == 'local') { 385 | $db = Capsule::connection()->getPdo(); 386 | } 387 | elseif ($sql_details['type'] == 'remote') { 388 | $db = db::get(); 389 | } 390 | else { 391 | self::fatal( 392 | "Unable to select an database connection." 393 | ); 394 | } 395 | } 396 | catch (PDOException $e) { 397 | self::fatal( 398 | "An error occurred while connecting to the database. ". 399 | "The error reported by the server was: ".$e->getMessage() 400 | ); 401 | } 402 | 403 | return $db; 404 | } 405 | 406 | 407 | /** 408 | * Execute an SQL query on the database 409 | * 410 | * @param resource $db Database handler 411 | * @param array $bindings Array of PDO binding values from bind() to be 412 | * used for safely escaping strings. Note that this can be given as the 413 | * SQL query string if no bindings are required. 414 | * @param string $sql SQL query to execute. 415 | * @return array Result from the query (all rows) 416 | */ 417 | static function sql_exec ( $db, $bindings, $sql=null ) 418 | { 419 | // Argument shifting 420 | if ( $sql === null ) { 421 | $sql = $bindings; 422 | } 423 | 424 | $stmt = $db->prepare( $sql ); 425 | //echo $sql; 426 | 427 | // Bind parameters 428 | if ( is_array( $bindings ) ) { 429 | for ( $i=0, $ien=count($bindings) ; $i<$ien ; $i++ ) { 430 | $binding = $bindings[$i]; 431 | $stmt->bindValue( $binding['key'], $binding['val'], $binding['type'] ); 432 | } 433 | } 434 | 435 | // Execute 436 | try { 437 | $stmt->execute(); 438 | } 439 | catch (PDOException $e) { 440 | self::fatal( "An SQL error occurred: ".$e->getMessage() ); 441 | } 442 | 443 | // Return all 444 | return $stmt->fetchAll( \PDO::FETCH_BOTH ); 445 | } 446 | 447 | 448 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 449 | * Internal methods 450 | */ 451 | 452 | /** 453 | * Throw a fatal error. 454 | * 455 | * This writes out an error message in a JSON string which DataTables will 456 | * see and show to the user in the browser. 457 | * 458 | * @param string $msg Message to send to the client 459 | */ 460 | static function fatal ( $msg ) 461 | { 462 | echo json_encode( array( 463 | "error" => $msg 464 | ) ); 465 | 466 | exit(0); 467 | } 468 | 469 | /** 470 | * Create a PDO binding key which can be used for escaping variables safely 471 | * when executing a query with sql_exec() 472 | * 473 | * @param array &$a Array of bindings 474 | * @param * $val Value to bind 475 | * @param int $type PDO field type 476 | * @return string Bound key to be used in the SQL where this parameter 477 | * would be used. 478 | */ 479 | static function bind ( &$a, $val, $type ) 480 | { 481 | $key = ':binding_'.count( $a ); 482 | 483 | $a[] = array( 484 | 'key' => $key, 485 | 'val' => $val, 486 | 'type' => $type 487 | ); 488 | 489 | return $key; 490 | } 491 | 492 | 493 | /** 494 | * Pull a particular property from each assoc. array in a numeric array, 495 | * returning and array of the property values from each item. 496 | * 497 | * @param array $a Array to get data from 498 | * @param string $prop Property to read 499 | * @return array Array of property values 500 | */ 501 | static function pluck ( $a, $prop ) 502 | { 503 | $out = array(); 504 | 505 | for ( $i=0, $len=count($a) ; $i<$len ; $i++ ) { 506 | $out[] = $a[$i][$prop]; 507 | } 508 | 509 | return $out; 510 | } 511 | 512 | 513 | /** 514 | * Return a string from an array or a string 515 | * 516 | * @param array|string $a Array to join 517 | * @param string $join Glue for the concatenation 518 | * @return string Joined string 519 | */ 520 | static function _flatten ( $a, $join = ' AND ' ) 521 | { 522 | if ( ! $a ) { 523 | return ''; 524 | } 525 | else if ( $a && is_array($a) ) { 526 | return implode( $join, $a ); 527 | } 528 | return $a; 529 | } 530 | } 531 | 532 | -------------------------------------------------------------------------------- /templates/admin_settings.tpl: -------------------------------------------------------------------------------- 1 | {assign var=records value=Controller::inConfig(record_types)} 2 | {assign var=status value=Controller::inConfig(auto_delete)} 3 | 4 |

{$LANG.admin_menu_settings}

5 | 6 |
7 |
8 | 9 |

{$LANG.admin_settings_allowed_records}

10 |
11 |
12 | 13 |
14 |
15 |
16 |
17 | 18 | 19 |
20 |
21 |
22 |
23 | 24 | 25 |
26 |
27 |
28 |
29 | 30 | 31 |
32 |
33 |
34 |
35 | 36 | 37 |
38 |
39 |
40 |
41 | 42 | 43 |
44 |
45 |
46 |
47 | 48 | 49 |
50 |
51 |
52 |
53 | 54 | 55 |
56 |
57 |
58 |
59 | 60 | 61 |
62 |
63 |
64 |
65 | 66 | 67 |
68 |
69 |
70 |
71 | 72 | 73 |
74 |
75 |
76 |
77 | 78 | 79 |
80 |
81 |
82 |
83 | 84 | 85 |
86 |
87 |
88 |
89 | 90 | 91 |
92 |
93 |
94 |
95 | 96 | 97 |
98 |
99 |
100 |
101 | 102 | 103 |
104 |
105 |
106 |
107 | 108 | 109 |
110 |
111 |
112 |
113 | 114 | 115 |
116 |
117 |
118 |
119 |
120 |

{$LANG.admin_settings_default_soa}

121 |
122 |
123 | 124 |
125 |
126 |
127 | 128 |
129 |
130 |
131 | 132 |
133 |
134 |
135 |
136 | 137 |
138 |
139 |
140 | 145 |
146 |
147 |
148 | 149 |
150 |
151 |
152 |
153 | 154 |
155 |
156 |
157 | 158 |
159 |
160 |
161 |
162 |
163 |
164 | 165 |
166 |
167 |
168 | 169 |
170 |
171 |
172 |
173 |
174 |
175 | 176 |
177 |
178 |
179 | 180 |
181 |
182 |
183 |
184 |
185 |
186 | 187 |
188 |
189 |
190 | 191 |
192 |
193 |
194 |
195 |
196 |
197 | 198 |
199 |
200 |
201 | 202 | 203 |
204 |
205 |
206 |
207 |

{$LANG.admin_settings_record_limits}

208 |
209 |
210 | 211 |
212 |
213 |
214 | 215 |
216 |
217 |
218 | 219 |
220 |
221 |
222 |

{$LANG.admin_settings_accessibility}

223 |
224 |
225 | 226 |
227 |
228 |
229 | 230 | 231 |
232 |
233 |
234 |
235 |
236 | 237 |
238 |
239 |
240 | 241 | 242 |
243 |
244 |
245 |
246 |
247 | 248 |
249 |
250 |
251 | 252 | 253 |
254 |
255 |
256 |
257 |
258 | 259 |
260 |
261 |
262 | 263 | 264 |
265 |
266 |
267 |
268 |
269 | 270 |
271 |
272 |
273 | 274 | 275 |
276 |
277 |
278 |
279 |
280 | 281 |
282 |
283 |
284 | 285 | 286 |
287 |
288 |
289 |
290 |
291 | 292 |
293 |
294 |
295 | 296 |
297 |
298 |
299 | 300 |
301 |
302 |
303 |

{$LANG.admin_settings_automation}

304 |
305 |
306 | 307 |
308 |
309 |
310 | 311 | 312 |
313 |
314 |
315 |
316 |
317 | 318 |
319 |
320 | 321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 | 330 | 331 |
332 |
333 |
334 |
335 | 336 | 337 |
338 |
339 |
340 |
341 | 342 | 343 |
344 |
345 |
346 |
347 | 348 | 349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 | 359 | 360 |
361 |
362 |
363 |
364 |
365 | 366 |
367 |
368 |
369 | 370 | 371 |
372 |
373 |
374 |
375 |
376 | 377 |
378 |
379 |
380 | 381 | 382 |
383 |
384 |
385 |

386 | 387 |
388 |
389 |
390 | -------------------------------------------------------------------------------- /hooks.php: -------------------------------------------------------------------------------- 1 | 26 | * @copyright NetDistrict 27 | * @link https://www.solutedns.com 28 | * */ 29 | 30 | /** 31 | * Required Classes. 32 | */ 33 | use WHMCS\Database\Capsule; 34 | use WHMCS\View\Menu\Item as MenuItem; 35 | use WHMCS\ClientArea; 36 | use WHMCS\Module\Addon\SoluteDNS\Admin\Controller as SDNS_Controller; 37 | use WHMCS\Module\Addon\SoluteDNS\System\Cron; 38 | use solutedns\Dns\Zones; 39 | 40 | $loader = new \Composer\Autoload\ClassLoader(); 41 | $loader->addPsr4('solutedns\\', __DIR__ . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'Core'); 42 | $loader->register(); 43 | 44 | /** 45 | * Automatically create zones 46 | * 47 | * Add's an field to the admin's domain details field for DNS Management. 48 | */ 49 | add_hook('AfterShoppingCartCheckout', 1, function($vars) { 50 | 51 | try { 52 | 53 | // Set Classes 54 | $zones = new Zones(); 55 | 56 | if (SDNS_Controller::Config('auto_create')) { 57 | 58 | // Set Params 59 | $domains = $vars['DomainIDs']; 60 | $services = $vars['ServiceIDs']; 61 | $entry = NULL; 62 | 63 | foreach ($domains as $domain_id) { 64 | 65 | $data = Capsule::table('tbldomains')->select('domain', 'dnsmanagement')->where('id', $domain_id)->first(); 66 | 67 | if ($data->dnsmanagement || SDNS_Controller::Config('auto_enabled')) { 68 | 69 | // Make sure DNS Management is enabled 70 | Capsule::table('tbldomains')->where('id', $domain_id)->update(['dnsmanagement' => '1',]); 71 | 72 | // Add entry for processing 73 | $entry[$domain]['domain'] = mb_strtolower($data->domain); 74 | } 75 | } 76 | 77 | foreach ($services as $service_id) { 78 | 79 | $data = Capsule::table('tblhosting')->select('domain', 'packageid')->where('id', $service_id)->first(); 80 | 81 | // Add entry for processing product records 82 | $domain = mb_strtolower($data->domain); 83 | $entry[$domain]['product'] = $data->packageid; 84 | } 85 | 86 | foreach ($entry as $create) { 87 | 88 | if (isset($create['domain'])) { 89 | 90 | // Get Records 91 | $product_id = isset($create['product']) ? $create['product'] : '0'; 92 | $records = json_encode(Capsule::table('mod_solutedns_template_records')->select('name', 'type', 'content', 'ttl', 'prio')->where('product_id', $product_id)->get()); 93 | 94 | // Create zone 95 | $send = [ 96 | 'domain' => $create['domain'], 97 | 'records' => json_decode(str_replace('{domain}', $create['domain'], $records), true), 98 | ]; 99 | 100 | $result = $zones->add($send); 101 | 102 | // Process result 103 | if (isset($result['success'])) { 104 | 105 | if (SDNS_Controller::ns_details('dnssec_rectify')) { 106 | SDNS_Controller::add_crontask($create['domain'], 'rectify'); 107 | } 108 | 109 | if (SDNS_Controller::Config('logging')) { 110 | logActivity('A new zone was automatically created for: ' . $create['domain'] . '.', 0); 111 | } 112 | } 113 | 114 | if (isset($result['dnssec']['errors'])) { 115 | 116 | foreach ($result['dnssec']['errors'] as $error) { 117 | 118 | $todo[] = 'DNS ERROR [' . $error['code'] . ']: ' . $error['desc']; 119 | if (SDNS_Controller::Config('logging')) { 120 | logActivity('DNS ERROR [' . $error['code'] . '] for ' . $create['domain'] . ': ' . $error['desc'], 0); 121 | } 122 | unset($error); 123 | } 124 | } 125 | 126 | if (isset($result['error'])) { 127 | 128 | $error = $result['error']; 129 | 130 | $todo[] = 'DNS ERROR [' . $error['code'] . ']: ' . $error['desc']; 131 | if (SDNS_Controller::Config('logging')) { 132 | logActivity('DNS ERROR [' . $error['code'] . '] for ' . $create['domain'] . ': ' . $error['desc'], 0); 133 | } 134 | unset($error); 135 | } 136 | 137 | // Add To-Do item 138 | if (isset($todo) && SDNS_Controller::Config('auto_todo')) { 139 | 140 | Capsule::table('tbltodolist')->insert( 141 | [ 142 | 'date' => date("Y-m-d"), 143 | 'title' => 'DNS action required for: ' . $create['domain'], 144 | 'description' => implode("\n", $todo), 145 | 'status' => 'New', 146 | 'duedate' => date("Y-m-d"), 147 | ] 148 | ); 149 | 150 | unset($todo); 151 | } 152 | } 153 | } 154 | } 155 | } catch (Exception $e) { 156 | logActivity('DNS ERROR [AfterShoppingCartCheckout]:' . $e->getMessage(), 0); 157 | } 158 | }); 159 | 160 | 161 | /** 162 | * Automatically delete zone 163 | * 164 | * Delete zone when the domain is deleted from WHMCS. 165 | */ 166 | add_hook('DomainDelete', 1, function($vars) { 167 | 168 | try { 169 | 170 | if (SDNS_Controller::Config('auto_delete_whmcs')) { 171 | 172 | // Check if no other registration exists 173 | $data = Capsule::table('tbldomains')->select('domain')->where('id', $vars['domainid'])->first(); 174 | $domain = mb_strtolower($data->domain); 175 | $count = Capsule::table('tbldomains')->where('domain', $data->domain)->count(); 176 | 177 | if ($count <= 1) { 178 | 179 | // Check if zone exists 180 | $zones = new Zones(); 181 | $result = $zones->exists($domain); 182 | 183 | if ($result == true) { 184 | 185 | // Remove zone 186 | $result = $zones->delete($domain); 187 | 188 | // Process result 189 | if (isset($result['success'])) { 190 | 191 | if (SDNS_Controller::Config('logging')) { 192 | logActivity('A zone was automatically deleted for: ' . $domain . '.', 0); 193 | } 194 | } 195 | if (isset($result['error'])) { 196 | 197 | $error = $result['error']; 198 | 199 | $todo[] = 'DNS ERROR [' . $error['code'] . ']: ' . $error['desc']; 200 | if (SDNS_Controller::Config('logging')) { 201 | logActivity('DNS ERROR [' . $error['code'] . '] for ' . $domain . ': ' . $error['desc'], 0); 202 | } 203 | } 204 | } 205 | } 206 | } 207 | } catch (Exception $e) { 208 | logActivity('DNS ERROR [DomainDelete]:' . $e->getMessage(), 0); 209 | } 210 | }); 211 | 212 | 213 | /** 214 | * Automatically delete zones on update 215 | * 216 | * Delete a zone when the domain status is edited to one of the configured statuses. 217 | */ 218 | add_hook('DomainEdit', 1, function($vars) { 219 | 220 | try { 221 | 222 | if (SDNS_Controller::Config('auto_delete')) { 223 | 224 | $status = SDNS_Controller::inConfig('auto_delete'); 225 | 226 | // Check if no other registration exists 227 | $data = Capsule::table('tbldomains')->select('domain', 'status')->where('id', $vars['domainid'])->first(); 228 | $domain = mb_strtolower($data->domain); 229 | $count = Capsule::table('tbldomains') 230 | ->where([['domain', $data->domain], ['status', 'Active']]) 231 | ->orWhere([['domain', $data->domain], ['status', 'Pending']]) 232 | ->orWhere([['domain', $data->domain], ['status', 'Pending Transfer']]) 233 | ->count(); 234 | 235 | if ($count <= 1) { 236 | 237 | // Check if status is configured 238 | if (in_array(strtolower($data->status), $status)) { 239 | 240 | // Check if zone exists 241 | $zones = new Zones(); 242 | $result = $zones->exists($domain); 243 | 244 | if ($result == true) { 245 | 246 | // Remove zone 247 | $result = $zones->delete($domain); 248 | 249 | // Process result 250 | if (isset($result['success'])) { 251 | 252 | if (SDNS_Controller::Config('logging')) { 253 | logActivity('A zone was automatically deleted for: ' . $domain . '.', 0); 254 | } 255 | } 256 | if (isset($result['error'])) { 257 | 258 | $error = $result['error']; 259 | 260 | $todo[] = 'DNS ERROR [' . $error['code'] . ']: ' . $error['desc']; 261 | if (SDNS_Controller::Config('logging')) { 262 | logActivity('DNS ERROR [' . $error['code'] . '] for ' . $domain . ': ' . $error['desc'], 0); 263 | } 264 | } 265 | } 266 | } 267 | } 268 | } 269 | } catch (Exception $e) { 270 | logActivity('DNS ERROR [DomainEdit]:' . $e->getMessage(), 0); 271 | } 272 | }); 273 | 274 | 275 | /** 276 | * Cron Tasks 277 | */ 278 | add_hook('AfterCronJob', 1, function($vars) { 279 | 280 | // Call cron class 281 | $cron = new Cron(); 282 | $cron->run(); 283 | }); 284 | 285 | 286 | /** 287 | * Menu Management 288 | * 289 | * Add's menu and sidebar entries to the client area. 290 | */ 291 | add_hook('ClientAreaPrimarySidebar', 1, function(MenuItem $primarySidebar) { 292 | 293 | try { 294 | 295 | if ($_SESSION['uid']) { 296 | 297 | // Set Domain ID and DNS Management state 298 | $tbldata = NULL; 299 | 300 | if (isset($_REQUEST['id'])) { 301 | 302 | $domain_id = (int) filter_input(INPUT_GET, "id", FILTER_SANITIZE_NUMBER_INT); 303 | 304 | $tbldata = Capsule::table('tbldomains')->select('dnsmanagement')->where('id', $domain_id)->first(); 305 | } 306 | 307 | // Get custom URL 308 | $custom_url = SDNS_Controller::config('client_urlrewrite'); 309 | $custom_url = !empty($custom_url) ? $custom_url . '/' : 'index.php?m=solutedns&id='; 310 | 311 | // Add Sidebar to DNS Management page 312 | if (App::getCurrentFilename() == 'index' && isset($_REQUEST['m']) && $_REQUEST['m'] == 'solutedns') { 313 | 314 | // Primary Sidebar 315 | if (is_null($primarySidebar->getChild('Domain Details Management'))) { 316 | 317 | $primarySidebar->addChild('Domain Details Management') 318 | ->setLabel(Lang::trans('manage')) 319 | ->setIcon('fa-cog'); 320 | 321 | $primarySidebar->getChild('Domain Details Management') 322 | ->addChild('Overview') 323 | ->setLabel(Lang::trans('overview')) 324 | ->setUri('clientarea.php?action=domaindetails&id=' . $domain_id . '#tabOverview') 325 | ->setOrder(10); 326 | 327 | $primarySidebar->getChild('Domain Details Management') 328 | ->addChild('Auto Renew Settings') 329 | ->setLabel(Lang::trans('domainsautorenew')) 330 | ->setUri('clientarea.php?action=domaindetails&id=' . $domain_id . '#tabAutorenew') 331 | ->setOrder(20); 332 | 333 | $primarySidebar->getChild('Domain Details Management') 334 | ->addChild('Domain Addons') 335 | ->setLabel(Lang::trans('domainaddons')) 336 | ->setUri('clientarea.php?action=domaindetails&id=' . $domain_id . '#tabAddons') 337 | ->setOrder(30); 338 | 339 | $primarySidebar->getChild('Domain Details Management') 340 | ->addChild('Manage DNS Host Records') 341 | ->setLabel(Lang::trans('domaindnsmanagement')) 342 | ->setUri($custom_url . $domain_id) 343 | ->setClass('active') 344 | ->setOrder(40); 345 | } 346 | } 347 | 348 | // Set or Update DNS Management menu item 349 | if (App::getCurrentFilename() == 'clientarea' && isset($_REQUEST['action']) && $_REQUEST['action'] == 'domaindetails') { 350 | 351 | if (!is_null($primarySidebar->getChild('Domain Details Management')->getChild('Manage DNS Host Records')) && empty(SDNS_Controller::config('respect_registrar'))) { 352 | 353 | $primarySidebar->getChild('Domain Details Management') 354 | ->getChild('Manage DNS Host Records') 355 | ->setUri($custom_url . $domain_id); 356 | } 357 | 358 | if (is_null($primarySidebar->getChild('Domain Details Management')->getChild('Manage DNS Host Records')) && $tbldata->dnsmanagement == 1) { 359 | 360 | $primarySidebar->getChild('Domain Details Management') 361 | ->addChild('Manage DNS Host Records') 362 | ->setLabel(Lang::trans('domaindnsmanagement')) 363 | ->setUri($custom_url . $domain_id) 364 | ->setOrder(100); 365 | } 366 | } 367 | } 368 | } catch (Exception $e) { 369 | logActivity('DNS ERROR [ClientAreaPrimarySidebar]:' . $e->getMessage(), 0); 370 | } 371 | }); 372 | 373 | /** 374 | * Redirect WHMCS DNS to SoluteDNS 375 | */ 376 | add_hook('ClientAreaPageDomainDNSManagement', 1, function($vars) { 377 | if ($_SESSION['uid'] && App::getCurrentFilename() == 'clientarea' && filter_input(INPUT_GET, 'action', FILTER_SANITIZE_SPECIAL_CHARS) == 'domaindns' && !empty(SDNS_Controller::config('force_dns'))) { 378 | 379 | $domain_id = filter_input(INPUT_GET, 'domainid', FILTER_VALIDATE_INT); 380 | 381 | if ($domain_id) { 382 | 383 | // Get custom URL 384 | $custom_url = !empty(SDNS_Controller::config('client_urlrewrite')) ? SDNS_Controller::config('client_urlrewrite') : NULL; 385 | 386 | // Get system URL 387 | $system_url = Capsule::table('tblconfiguration')->select('value')->where('setting', 'SystemURL')->first()->value; 388 | 389 | // Redirect to SoluteDNS 390 | $url = !is_null($custom_url) ? $system_url.$custom_url.'/'.$domain_id : 'index.php?m=solutedns&id='.$domain_id; 391 | 392 | header('Location: '.$url, true, '302'); 393 | exit(); 394 | 395 | } 396 | 397 | } 398 | 399 | }); 400 | 401 | /** 402 | * Admin Tab Field 403 | * 404 | * Add's an field to the admin's domain details field for DNS Management. 405 | */ 406 | add_hook('AdminClientDomainsTabFields', 1, function($vars) { 407 | 408 | return [ 409 | Lang::trans('domaindnsmanagement') => ' Manage', 410 | ]; 411 | }); 412 | 413 | /** 414 | * Admin HTML header 415 | * 416 | * Add's required HTML resources to the head for SoluteDNS pages. 417 | */ 418 | add_hook('AdminAreaHeadOutput', 1, function($vars) { 419 | 420 | try { 421 | 422 | if (!isset($_GET['module']) || $_GET['module'] != 'solutedns') { 423 | return NULL; 424 | } 425 | 426 | $paging = SDNS_Controller::Config('dns_pagination') ? 'true' : 'false'; 427 | 428 | return 429 | << 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 480 | HTML; 481 | } catch (Exception $e) { 482 | logActivity('DNS ERROR [AdminAreaHeadOutput]:' . $e->getMessage(), 0); 483 | } 484 | }); 485 | 486 | /** 487 | * Client HTML header 488 | * 489 | * Add's required HTML resources to the head for SoluteDNS pages. 490 | */ 491 | add_hook('ClientAreaHeadOutput', 1, function($vars) { 492 | 493 | try { 494 | 495 | if ($_SESSION['uid']) { 496 | 497 | if ( 498 | $vars['filename'] == 'index' && App::isInRequest('m') && App::getFromRequest('m') == 'solutedns' 499 | ) { 500 | 501 | // Get custom URL 502 | if (substr($_SERVER[REQUEST_URI],-1) == '/') { 503 | $custom_url = !empty(SDNS_Controller::config('client_urlrewrite')) ? '../' : NULL; 504 | } else { 505 | $custom_url = NULL; 506 | } 507 | 508 | 509 | // Set paging 510 | $paging = SDNS_Controller::Config('dns_pagination') ? 'true' : 'false'; 511 | 512 | return 513 | << 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 564 | HTML; 565 | } else { 566 | return NULL; 567 | } 568 | } 569 | } catch (Exception $e) { 570 | logActivity('DNS ERROR [ClientAreaHeadOutput]:' . $e->getMessage(), 0); 571 | } 572 | }); 573 | --------------------------------------------------------------------------------