mode = $mode; } public function get_client($throw = false) { return Auth\OneCrm::instance()->getContactClient($throw); } public function register() { $config_options = get_option('onecrm_p_options'); Locale::Instance($config_options["locale"]); // register the codes we'll service: add_shortcode('onecrm_p_dashboard', [$this, 'dashboard_shortcode']); add_shortcode('onecrm_p_plan_selection',[$this, 'plan_selection_shortcode']); add_shortcode('onecrm_p_payments_register', [$this, 'payments_register_shortcode']); add_shortcode('onecrm_p_payments_choose', [$this, 'payments_choose_shortcode']); add_shortcode('onecrm_p_payments_verify', [$this, 'payments_verify_shortcode']); $this->enqueue_application_scripts(); Subscription::register(); // delegate subscription-related shortcodes } /** * Presents / selects available Plans / Addons / OptionSets * @param $attr * @return string|bool */ public function plan_selection_shortcode($attr){ if (!$client = Auth\OneCrm::instance()->getAdminClient(false)) return false; ob_start(); $attr['processor'] = 'ChargeBee'; // hard-coded for now. if (! $attr['plans']??null) { return __("No Plans Selected", ONECRM_P_TEXTDOMAIN); } $pp_plan_ids = explode(',', $attr['plans']); $filter_coupons=[]; $filter_addons=[]; $filter_options=[]; try { $modelFilters = [ 'Coupon' => [ ['field' => 'active', 'value' => 'Active'], ['operator' => 'OR', 'multiple' => [ ['field' => 'pp_coupon_id', 'operator' => 'IN', 'value' => & $filter_coupons], ['field' => 'addon_mode', 'value' => 'All Addons'], ['field' => 'plan_mode', 'value' => 'All Plans'], ]] ], 'Addon' => [ ['field' => 'active', 'value' => 'Active',], ['operator' => 'OR', 'multiple' => [ ['field' => 'pp_addon_id', 'operator' => 'IN', 'value' => & $filter_addons], ['field' => 'global', 'value' => '1'], ]] ], 'Option' => [ ['field' => 'option_number', 'operator' => 'IN', 'value' => & $filter_options], ], ]; if ($attr['plans']??null) $modelFilters['Plan'] = [ [ 'field' => 'active', 'value' => 'Active'], [ 'field'=>'pp_plan_id', 'operator'=> 'IN', 'value' => $pp_plan_ids], ]; foreach(['pp_plan_id'=>'Plan', 'pp_addon_id'=>'Addon', 'pp_coupon_id'=>'Coupon', 'option_number'=>'Option'] as $key=> $model){ $modelClient = $client->subscription($model,$attr['processor']); if (!empty(@$modelFilters[$model])) { $filters = ['_lq'=>$modelFilters[$model]]; } else { $filters = ['active' => 'Active']; } $result = $modelClient->getlist([ 'fields' => [true], // all allowed fields 'filters' => $filters, ]); $list=$result->getRecords(); foreach (['meta', 'pricing_tiers', 'addons', 'coupons', 'options'] as $field){ if (!array_key_exists($field, current($list))) continue; if ($model == "Option" && $field == "options") continue; foreach($list as &$row){ $row[$field] = json_decode($row[$field],true); if ($field == 'meta' && is_array($row['meta'])){ $row += $row['meta']; unset($row['meta']); } if ($row[$field]==[]) { $row[$field] = null; } elseif ($field=='addons'){ $filter_addons += $row['addons']; } elseif ($field == 'coupons'){ $filter_coupons += $row['coupons']; } elseif ($field == 'options'){ $filter_options = array_unique(array_merge($filter_options, $row[$field])); } } } $lists[$model] = array_combine(array_column($list, $key), $list); } $processor_id = $modelClient->get_processor_id(); /* debug dump: echo onecrm_debug_var("JSON", $lists);/**/ } catch (APIError $e){ echo stripslashes( onecrm_debug_var(['value'=>$e,'args'=>1, ])); } if (count($lists['Plan']) > 0){ // present them in the order set in the shortcode: $tmp=[]; foreach($lists['Plan'] as $plan) { $tmp[strtolower($plan['pp_plan_id'])] = $plan; } $lists['Plan'] = $tmp; } else return __("No Plans Selected", ONECRM_P_TEXTDOMAIN); // inject the template and plan data: echo('
'); $template = preg_replace('/[^a-z0-9._-]/i','',$attr['template']??'plan_selection'); echo file_get_contents(ONECRM_P_PATH."/templates/html/$template.html"); $this->enqueue_page_data('window.OneCRM.Portal.App.replace',$lists); echo "
"; // end app widget: // if there's a master plan then wrap it as the list: $source = ($attr['master']??null) ? "[R.Plan[\"{$attr['master']}\"]]": "R.Plan"; // inject the trigger to populate the selection widget: $this->enqueue_page_scripts('plan_widget_init', /** @lang JavaScript */ <<TODO: Payments Register Shortcode"; } /** * Selects a payment method to finalize the purchase/subscription * @param $attr * @return string */ public function payments_choose_shortcode($attr){ return "

TODO: Payments Choose Shortcode

"; } /** * Waits for payment notification from PP and shows thankyou or rejection page * @param $attr * @return string */ public function payments_verify_shortcode($attr){ return "

TODO: Payments Verify Shortcode

"; } /** * called to add the datetime picker support resources */ public function enqueue_datetime_scripts(){ static $once=false; if (! $once) { add_action('wp_footer',function() { // load this as late as possible: wp_enqueue_style('jquery-ui-css', ONECRMP_PLUGIN_URL . '/css/jquery-ui.min.css'); wp_enqueue_style('jquery-ui-timepicker-addon', ONECRMP_PLUGIN_URL . '/css/jquery-ui-timepicker-addon.min.css'); wp_enqueue_script('jquery-ui-timepicker-addon', ONECRMP_PLUGIN_URL. '/js/jquery-ui-timepicker-addon.min.js', array('jquery','jquery-ui-core','jquery-ui-datepicker')); wp_enqueue_script('jquery-ui-sliderAccess', ONECRMP_PLUGIN_URL. '/js/jquery-ui-sliderAccess.js'); wp_enqueue_script('onecrm-p-editor-events', ONECRMP_PLUGIN_URL . '/js/editor-events.js'); }); $once=true; } } /** * Aggregates jQuery.extend(target,data) in script blocks, ordered by id and call order * Use only for page-specific data. * @param string $target ie 'window.OneCRM.App.Portal.replace' * @param array $data * @param string $id - id of the script tag to aggregate in * @return bool false if $name isn't string or $data isn't object */ public function enqueue_page_data($target, $data, $id = "onecrmPortalApp"){ if (!(is_array($data)|| is_object($data))) return false; // ~=20ms for 10 plans with about 10 shared coupons/addons $data = onecrm_html_sanitizer_r($data); $data = json_encode($data, JSON_PRETTY_PRINT); return $this->enqueue_page_scripts($id, "$.extend(true,$target, $data);",'ready'); } /** * Ordered by template, script, ready; then name, order called * Use only for page-specific content, or short activation scripts. * @param string $id of the injected script tag. multiple calls aggregate * @param string $content * @param string $type * @param int $priority >=11, set on the first call only * @return bool */ public function enqueue_page_scripts($id, $content, $type = 'ready', $priority = 20){ static $app_scripts=['template'=>[],'script'=>[],'ready'=>[]]; static $registered = false; if (!(is_string($content) && is_string($id))) return false; $this->enqueue_application_scripts(); $app_scripts[$type][$id][]=$content; if (!$registered){ $registered = true; add_action('wp_print_footer_scripts',function () use (&$app_scripts) { foreach ($app_scripts as $type => &$ids){ ksort($ids); foreach ($ids as $id => $contents){ $attr = ($type=='template') ? ' type="text/template"' : ''; $attr .= ' id="'.htmlspecialchars($id).'"'; echo "".PHP_EOL; if ($type==="ready") { echo 'jQuery(function($){'; } elseif ($type==="data" || $type=== "script") { echo '(function($){'; } echo implode(PHP_EOL,$contents); if ($type==="ready") { echo "});"; } elseif ($type === "data" || $type === "script") { echo '})(jQuery);'; } echo ''.PHP_EOL; } } unset($names); }, $priority); } return true; } public function enqueue_application_scripts(){ static $once=false; if (! $once) { add_action('wp_footer', function () { // load this as late as possible: wp_enqueue_style('onecrm-portal-app', ONECRMP_PLUGIN_URL . '/css/OneCRM.Portal.css'); wp_enqueue_script('onecrm-portal-app', ONECRMP_PLUGIN_URL . '/js/OneCRM.Portal.js'); }); $once = true; } } /* * Sample implementation of Content wrapper */ public function onecrm_start_shortcode($attr) { $style = htmlspecialchars($attr['style']); return "
"; } public function onecrm_end_shortcode($attr) { return "
"; } /* * Sample implementation of Editor Form wrapper * submit action sends the form and dumps the server response to the console * if no action is given it will be directed to an echo facility for testing * * @param $attr[action] = ajax action (onecrm_ajax_echo) */ public function onecrm_editor_start_shortcode($attr) { // default action: $attr = (array)$attr; $URL = admin_url('admin-ajax.php') . '?action=' . $attr['action']; $html = '
'; $html .= '
'; return $html; } /* * the script should be enqueued from a file, but this is just WIP debug code... */ public function onecrm_editor_end_shortcode($attr) { $content = '
'; if (@$attr["submit"]) { $content = '' . $content; } return $content; } /* dump a model as an HTML fieldset, as part of an Editor Form: * * a proper implementation will take the Model and ID from GET args * and also offer a way to restrict and order the fields rendered. * * @param $attr[model] 1CRM API model name ie Contact * @param $attr[id] model GUID to render a form for * @param $attr[fields] list of field names to render * @param $attr[classes] hash of field names to class names to add to the field cells */ public function onecrm_fieldset_shortcode($attr) { if (!$client = $this->get_client()) return false; try { $attr = (array)$attr; if (empty($attr['columns'])) $attr['columns'] = 3; $model_name = @$attr['model']; $id = @$attr['id']; if (!$model_name) { return false; } $model = $client->model($model_name); $fields_meta = $model->metadata(); if (!@$attr['fields']) { // start with all possible fields: $fields = array_keys($fields_meta["fields"]); asort($fields); $fields = array_values(["name"] + $fields); } else { $fields = $attr['fields']; if (!is_array($fields)) $fields = explode(',', $fields); } // shortcode classes arg can specify additional class names for some fields: */ $field_classes = []; parse_str(@$attr["classes"], $field_classes); foreach ($field_classes as $key => $value) { $fields_meta["fields"][$key]["class"] = $value; } if (!empty($attr['id'])) $row = $model->get(@$attr["id"], $fields); else $row = []; } catch (APIError $e) { return '
' . __("Unable to load record", ONECRM_P_TEXTDOMAIN) . '
' ; } $this->enqueue_datetime_scripts(); ob_start(); $EditView = new Renderer\EditView(['detail_available' => null, 'model' => $model_name, 'columns' => $attr['columns'], 'format_input' => $fields, 'format_input_prefix' => $model_name . '[', 'format_input_postfix' => ']']); $post = get_post(); echo $EditView->render(compact('row', 'fields', 'fields_meta')); echo $EditView->create_hidden_input($model_name . '[id]', $id); echo $EditView->create_hidden_input('model', $model_name); echo $EditView->create_hidden_input('ret', $post->ID); return ob_get_clean(); } /* * Sample implementation of DetailView as shortcode * Not intended for production. */ public function detailview_shortcode($attr, $show_empty = true) { if (!$client = $this->get_client(true)) return false; try { $attr = (array)$attr; $link_template_data = isset($attr['link_template_data']) ? $attr['link_template_data'] : null; $link_template = isset($attr['link_template']) ? $attr['link_template'] : '?model={MODEL}&id={ID}'; $columns = isset($attr["columns"])?$attr['columns']:3; $detail_available = !empty($attr["detail_available"]); $model_name = $attr["model"]; $model = $client->model($model_name); $fields_meta = $client->get("/meta/fields/$model_name"); if ($model_name == 'Invoice') $this->add_cutom_field_metadata($fields_meta, 'paypal_button'); $fields = array_keys($fields_meta["fields"]); if (@$attr['fields']) { $f = $attr['fields']; if (!is_array($f)) $f = explode(',', $f); $fields = array_values(array_intersect($f, $fields)); } $filter = compact("fields"); if (@$attr["start"]) { $rows = $model->getList($filter, $attr["start"], 1)->getRecords(); $row = $rows[0]; } elseif (@$attr["id"]) { $row = $model->get(@$attr["id"], $fields); } else { return false; } } catch (APIError $e) { return '
' . __("Unable to load record", ONECRM_P_TEXTDOMAIN) . '
' ; } $this->add_custom_fields($model_name, $fields, $fields_meta, $row); if (! $show_empty) { $row = array_filter($row); $fields = array_filter( $fields, function($el) use ($row) { return isset($row[$el]); } ); } // test of default link template and default link data from meta/row, and customer columns $renderer_options = [ "detail_available" => $detail_available, "model" => $model_name, "id" => $row['id'], "columns" => $columns, 'link_template_data' => $link_template_data, 'link_template' => $link_template, ]; $title = isset($attr['title']) ? $attr['title'] : $model_name; $title = !empty($attr['title_escaped']) ? $title : htmlspecialchars($title); ob_start(); echo "

$title

"; // order the fields by field name: $render_type = isset($attr['render_type']) ? $attr['render_type'] : ''; $DetailView = $this->get_detail_renderer($renderer_options, $render_type); echo $DetailView->render(compact("row", "fields", "fields_meta")); return ob_get_clean(); } /** * Get DetailView Renderer by type * * @param array $options * @param string $type: renderer type - 'erase_personal' or '' by default * * @return PersonalDataEraseView|Renderer\DetailView */ private function get_detail_renderer($options, $type = '') { if ($type == 'erase_personal') { $this->enqueue_datetime_scripts(); return new PersonalDataEraseView($options); } else { return new Renderer\DetailView($options); } } /* * Sample implementation of ListView as shortcode * Not intended for production. */ public function listview_shortcode($attr, $content = null, $shortcode = null) { try { !$client = $this->get_client(true); if (!$client) return false; } catch (Auth\ConfigError $e) { return $this->displayAuthErrors($e->getErrors()); } $link_template_data = isset($attr['link_template_data']) ? $attr['link_template_data'] : true; $link_template = isset($attr['link_template']) ? $attr['link_template'] : '?model={MODEL}&id={ID}'; $model_name = $attr["model"]; $link_name = @$attr["link"]; $parent_model = @$attr["parent_model"]; $parent_id = @$attr["parent_id"]; $no_restrictions = @$attr['no_restrictions']; $start = (int)@$attr["start"]; if ($start < 0) $start = 0; $count = (int)@$attr["count"]; if ($count < 1) $count = 20; try { $title = isset($attr['title']) ? $attr['title'] : $model_name; $title = !empty($attr['title_escaped']) ? $title : htmlspecialchars($title); $detail_available = isset($attr['detail_available']) && is_callable($attr['detail_available']) ? $attr['detail_available'] : true; if ($no_restrictions) { $client = Auth\OneCrm::instance()->getAdminClient(); if (!$client) return false; } $model = $client->model($model_name); $fields_meta = $client->get("/meta/fields/$model_name"); $filter = ['fields' => array_keys($fields_meta['fields'])]; $fields = isset($attr['fields']) ? $attr['fields'] : null; $order = isset($attr['order']) ? $attr['order'] : null; if (is_null($fields)) { $fields = array_keys($fields_meta['fields']); } if (is_string($fields)) { $fields = explode(',', $fields); } if (isset($attr['filters']) && is_array($attr['filters'])) { $filter['filters'] = $attr['filters']; } //Add filters dat from user's input $this->add_input_filters($filter, $attr['model_list_filters'], $fields_meta['filters']); $filter['order'] = $order; if ($link_name) { $related_model = $client->model($parent_model); $list = $related_model->getRelated($parent_id, $link_name, $filter, $start, $count); } else { $list = $model->getList($filter, $start, $count); } $rows = $list->getRecords(); $totalRows = $list->totalResults(); } catch (APIError $e) { return __("Unable to load list for", ONECRM_P_TEXTDOMAIN).' '.$model_name; } if (!empty($attr['paginate'])) { $curPage = (int)($start / $count) + 1; $pages = new Renderer\Pagination($curPage, $count, $totalRows, '?full_list=' . $model_name . '&page_number=%s'); } ob_start(); echo "

$title

"; if ($attr['limit_model'] && ! empty($attr['model_list_filters'])) { $this->enqueue_datetime_scripts(); $FilterFormView = new FilterFormView([]); echo $FilterFormView->render( [ 'filters' => $attr['model_list_filters'], 'input' => onecrm_p_sanitize_postdata_multiline($_POST), 'def' => $fields_meta['filters'], 'model' => $model_name, 'create_button' => @$attr['create_button'], 'page_url' => $_SERVER['REQUEST_URI'] ] ); } if (!empty($attr['paginate'])) echo $pages->render(); // test of custom link template and link_template_data callback: $ListView = new Renderer\ListView([ "detail_available" => $detail_available, "model" => $model_name, "link_template" => $link_template, "download_url" => @$attr['download_url'], "link_template_data" => $link_template_data ]); $this->add_custom_fields($model_name, $fields, $fields_meta, $rows); echo $ListView->render(compact("rows", "fields", "fields_meta")); if (!empty($attr['paginate'])) echo $pages->render(); return ob_get_clean(); } public function dashboard_shortcode($attr, $content = null, $shortcode = null) { if (is_null($this->is_partner)) { $this->query_partner_status(); } $model = onecrm_get_default($attr, 'model'); $full_list = sanitize_text_field(onecrm_get_default($_GET, 'full_list')); if ($full_list && $model) $full_list = $model; $detailview = sanitize_text_field(onecrm_get_default($_GET, 'detailview')); if ($detailview && $model) $detailview = $model; $create = sanitize_text_field(onecrm_get_default($_GET, 'create')); //if ($create && $model) $create = $model; $edit = sanitize_text_field(onecrm_get_default($_GET, 'edit')); //if ($edit && $model) $edit = $model; $personal_data = sanitize_text_field(onecrm_get_default($_GET, 'personal_data')); //if ($personal_data && $model) $edit = $model; if (!$create && !$edit && !$detailview && ! $personal_data && $model) $full_list = $model; $html = $this->onecrm_start_shortcode([]); if ($personal_data) { $html .= $this->dashboard_personal_data($personal_data, sanitize_key($_GET['record']), sanitize_key($_GET['format']), !!$model); } elseif ($create) { $html .= $this->dashboard_create($create, !!$model); } elseif ($edit) { $html .= $this->dashboard_edit($edit, sanitize_key($_GET['record']), !!$model); } elseif ($full_list) { $html .= $this->dashboard_index($full_list, !!$model); } elseif ($detailview) { $html .= $this->dashboard_detail($detailview, sanitize_key($_GET['record']), !!$model); } else { $html .= $this->dashboard_index(); } $html .= $this->onecrm_end_shortcode([]); return $html; } public function dashboard_index($limit_model = null, $no_breadcrumbs = false) { $all = onecrm_p_get_all_modules(); $saved = get_option('onecrm_p_dashboard_config'); if ($limit_model) { $limit_module = onecrm_p_module_for_model($limit_model); } else { $limit_module = null; } $modules = array_filter($saved, function($m) use($limit_model, $limit_module) { return $m['enabled'] && (!$limit_model || $limit_module === $m['module']); }); $html = ''; $detail_available = function($m) use($all) { return count(array_filter($all, function($d) use($m) { return $d['model'] === $m; })); }; $link_template_data = function($model = "", $id = "") { return ["MODEL" => $model, "RECORD" => $id]; }; foreach ($modules as $m) { $fields = array_map(function($f) {return $f['field'];}, array_filter($m['list'], function($f) { return $f['enabled'];}) ); $defs = array_filter($all, function($d) use ($m) { return $d['module'] === $m['module'];}); if (!$defs) continue; $def = current($defs); if (!empty($def['partners_only']) && !$this->is_partner) { continue; } if ($limit_model && !$no_breadcrumbs) { $title = '' . __("Dashboard", ONECRM_P_TEXTDOMAIN) . ' » ' . htmlspecialchars($def['plural']); } else { $title = '' . htmlspecialchars($def['plural']) . ''; } $attrs = [ 'model' => $def['model'], 'title' => $title, 'title_escaped' => true, 'fields' => join(',', $fields), 'detail_available' => $detail_available, 'link_template_data' => $link_template_data, 'link_template' => '?detailview={MODEL}&record={ID}', 'limit_model' => $limit_model ]; if (!empty($def['add_filters'])) $attrs['model_list_filters'] = $def['add_filters']; if (!empty($def['can_create'])) { $create_url = '?create=' . $def['model']; $create_button = ' '; //If model has list filters, add create button in the FilterFormView //otherwise add create button to title if (!empty($def['add_filters'])) { $attrs['create_button'] = $create_button; } else { $attrs['title'] .= $create_button; } } if ($limit_model) { $page = (int)sanitize_key($_GET['page_number']); if ($page < 1) $page = 1; $perPage = 20; $attrs['count'] = $perPage; $attrs['start'] = ($page - 1) * $perPage; $attrs['paginate'] = true; } $lv = $this->listview_shortcode($attrs); if (strpos($lv, 'onecrm-p-exception') !== false) { return $lv; } $html .= $lv; } return $html; } public function dashboard_detail($model, $id, $no_breadcrumbs = false) { $html = ''; $all = onecrm_p_get_all_modules(); $saved = get_option('onecrm_p_dashboard_config'); $module = onecrm_p_module_for_model($model); $modules = array_filter($saved, function($m) use($module) { return $m['enabled'] && $module === $m['module']; }); if (empty($modules)) { return $html; } $module = current($modules); $defs = array_filter($all, function($d) use ($module) { return $d['module'] === $module['module'];}); $def = current($defs); if (!empty($def['partners_only']) && !$this->is_partner) { return $html; } $detail_available = function($m) use($all) { return count(array_filter($all, function($d) use($m) { return $d['model'] === $m; })); }; $link_template_data = function($model = "", $id = "") { return ["MODEL" => $model, "RECORD" => $id]; }; $fields = array_map(function($f) {return $f['field'];}, array_filter($module['detail'], function($f) { return $f['enabled'];}) ); $title = ''; if (!$no_breadcrumbs) { $title .= '' . __("Dashboard", ONECRM_P_TEXTDOMAIN) . ' » '; } $title .= '' . htmlspecialchars($def['plural']) . ''; if (! empty($def['pdf_print'])) $title .= $this->render_pdf_link($model, $id); $attrs = [ 'model' => $model, 'id' => $id, 'fields' => $fields, 'title_escaped' => true, 'title' => $title, 'detail_available' => $detail_available, 'link_template_data' => $link_template_data, 'link_template' => '?detailview={MODEL}&record={ID}', 'columns' => 2, ]; $dv = $this->detailview_shortcode($attrs); if ($dv === false) { return '
' . __('Customer Connection is not properly configured', ONECRM_P_TEXTDOMAIN) . '
' ; } if (!empty($def['items'])) $dv .= $this->render_items($model, $id); if (!empty($def['display_contacts'])) $dv .= $this->render_contacts($model, $id); if (!empty($def['display_notes'])) { $dv .= $this->render_notes($model, $id, !empty($def['add_notes'])); if (!empty($def['add_notes'])) { $dv .= '
'; $dv .= '
'; $ev = new Renderer\EditView([ 'detail_available' => null, 'model' => 'Note', 'columns' => 2, 'format_input' => $fields, ]); $dv .= $ev->render([ 'row' => [], 'fields' => ['name', 'file', 'description'], 'format_input' => ['file', 'name', 'description'], 'fields_meta' => [ 'fields' => [ 'file' => [ 'name' => 'file', 'type' => 'file_ref', 'vname' => __('Attachment (optional)', ONECRM_P_TEXTDOMAIN), ], 'name' => [ 'name' => 'name', 'type' => 'varchar', 'len' => 40, 'vname' => __('Note title', ONECRM_P_TEXTDOMAIN), 'required' => true, ], 'description' => [ 'name' => 'description', 'type' => 'text', 'vname' => __('Details', ONECRM_P_TEXTDOMAIN), ], ], ], ]); $dv .= '

'; list ($req, $signature) = onecrm_signed_request([ 'module' => $module['module'], 'id' => $id, 'return' => $_SERVER['REQUEST_URI'], ]); $dv .= $ev->create_hidden_input('request', $req); $dv .= $ev->create_hidden_input('token', $signature); $dv .= $ev->create_hidden_input('onecrm_post_action', 'create_note'); $dv .= '
'; } } if ($model == 'Project') $dv .= $this->render_project_tasks($model, $id); if ($model == 'Invoice') $dv .= $this->render_payments($model, $id); return $dv; } private function render_pdf_link($model, $id, $personal_data = false) { $pdf_url = '?onecrm_pdf_generate='.$id.'&model='.$model.'&token=' . sha1(wp_get_session_token() . $id); if ($personal_data) $pdf_url .= '&personal_data=1'; $link = ''; return $link; } private function render_items($model, $id) { if (!$client = $this->get_client()) return false; $all = onecrm_p_get_all_modules(); $saved = get_option('onecrm_p_dashboard_config'); $module = onecrm_p_module_for_model($model); $modules = array_filter($saved, function($m) use($module) { return $m['enabled'] && $module === $m['module']; }); if (empty($modules)) { return ""; } $module = current($modules); $fields = array_map(function($f) {return $f['field'];}, array_filter($module['items'], function($f) { return $f['enabled'];}) ); $ret = ''; try { $tally = $client->get("/tally/{$model}/{$id}"); $multigroup = count($tally['groups']) > 1; $gnum = 1; foreach ($tally['groups'] as $g) { if (!strlen($g['name'])) { $g['name'] = sprintf( __( 'Group %d' /* translators: This is default quote/invoice group name. If a group name is empty, default name will be generated. %d is the group namber */ , ONECRM_P_TEXTDOMAIN ), $gnum); } $gnum++; $gname = $multigroup ? $g['name'] : ''; $ret .= '

' . htmlspecialchars($gname) . '

'; $items = array_reduce($g['line_items'], function($carry, $item) { $carry[] = $item; if (!empty($item['sum_of_components'])) { foreach ($item['components'] as $comp) { $comp['name'] = '    ' . $comp['name']; $carry[] = $comp; } } return $carry; }, []); foreach ($items as &$li) { if (empty($li['is_comment'])) { $li['unit_price'] = Locale::format_currency($li['unit_price'], "symbol", false, '{amount}'); $li['ext_price'] = Locale::format_currency($li['ext_price'], "symbol", false, '{amount}'); $li['quantity'] = Locale::format_number($li['quantity']); } } $ListView = new Renderer\ListView([ "detail_available" => false, ]); $ret .= $ListView->render([ 'rows' => $items, 'fields' => $fields, 'fields_meta' => ['fields' => onecrm_p_get_items_fields()], ]); if ($multigroup) { $ret .= '
'; $ret .= sprintf(__('Subtotal: %0.2f' /* translators: %f will be replaced with group subtotal */, ONECRM_P_TEXTDOMAIN), $g['subtotal']); $ret .= '
'; foreach ($g['adjusts'] as $adj) { switch ($adj['type']) { case 'StandardTax': if ($adj['value'] >= 0.01) { $ret .= '
'; $ret .= htmlspecialchars(sprintf(__( '%1$s (%2$0.2f%%): %3$0.2f' /* translators: 3 arguments: %1$s will be replaced with tax name, %2$0.2f will be replaced with tax rate, second %3$0.2f will be replaced with tax amount. Note % after first %f! */ , ONECRM_P_TEXTDOMAIN), $adj['name'], $adj['rate'], $adj['value'])); $ret .= '
'; } break; case 'TaxedShipping': case 'UntaxedShipping': if ($adj['value'] >= 0.01) { $ret .= '
'; $ret .= htmlspecialchars(sprintf( __( 'Shipping: %0.2f' /* translators: %0.2f will be replaced with shipping amount */ , ONECRM_P_TEXTDOMAIN ), $adj['value'])); $ret .= '
'; } break; } } $ret .= '
'; $ret .= sprintf(__('Total: %0.2f' /* translators: %0.2f will be replaced with quote/invoice total amount */ , ONECRM_P_TEXTDOMAIN), $g['total']); $ret .= '
'; } } if ($multigroup) { $ret .= '
'; } $ret .= '

' . __('Totals', ONECRM_P_TEXTDOMAIN) . '

'; $ret .= '
'; $ret .= sprintf(__('Subtotal: %0.2f', ONECRM_P_TEXTDOMAIN), $tally['totals']['subtotal']); $ret .= '
'; if ($tally['totals']['discount'] >= 0.01) { $ret .= '
'; $ret .= sprintf(__('Discount: %0.2f' /* translators: %0.2f will be replaced with discount amount */, ONECRM_P_TEXTDOMAIN), $tally['totals']['discount']); $ret .= '
'; } foreach ([true, false] as $t) { if ($tally['totals']['shipping_taxed'] == $t && $tally['totals']['shipping'] >= 0.01) { $ret .= '
'; $ret .= sprintf(__('Shipping: %0.2f', ONECRM_P_TEXTDOMAIN), $tally['totals']['shipping']); $ret .= '
'; } if ($tally['totals']['shipping_taxed'] != $t) { foreach ($tally['totals']['taxes'] as $tax) { if ($tax['amount'] >= 0.01) { $ret .= '
'; $ret .= htmlspecialchars(sprintf(__('%1$s (%2$0.2f%%): %3$0.2f', ONECRM_P_TEXTDOMAIN), $tax['name'], $tax['rate'], $tax['amount'])); $ret .= '
'; } } } } $ret .= '
'; $ret .= sprintf(__('Total: %0.2f', ONECRM_P_TEXTDOMAIN), $tally['totals']['total']); $ret .= '
'; } catch (APIError $e) { return $e->getMessage(); } return $ret; } private function render_contacts($parent_model, $parent_id) { $result = '
'; $result .= $this->listview_shortcode([ 'model' => 'Contact', 'parent_model' => $parent_model, 'link' => 'contacts', 'parent_id' => $parent_id, 'no_restrictions' => true, 'title' => __('Contacts', ONECRM_P_TEXTDOMAIN), 'fields' => ['name','email1', 'phone_work', 'primary_account'], 'detail_available' => function() {return false;}, 'count' => 200, ]); return $result; } private function render_notes($parent_model, $parent_id, $add_note = false) { $result = '
'; $title = __('Notes', ONECRM_P_TEXTDOMAIN); if ($add_note) $title .= ''; $result .= $this->listview_shortcode([ 'model' => 'Note', 'parent_model' => $parent_model, 'link' => 'notes', 'parent_id' => $parent_id, 'no_restrictions' => true, 'title' => $title, 'title_escaped' => true, 'fields' => ['name', 'description', 'filename'], 'detail_available' => function() {return false;}, 'count' => 200, 'download_url' => function($model, $id) { $token = wp_get_session_token(); return '?onecrm_file_download=' . $id . '&token=' . sha1($token . $id); }, 'filters' => [ 'portal_flag' => 1, ], 'order' => 'date_entered', ]); return $result; } private function render_project_tasks($parent_model, $parent_id) { $result = '
'; $result .= $this->listview_shortcode([ 'model' => 'ProjectTask', 'parent_model' => $parent_model, 'link' => 'project_tasks', 'parent_id' => $parent_id, 'no_restrictions' => true, 'title' => __('Tasks', ONECRM_P_TEXTDOMAIN), 'fields' => ['name', 'date_start', 'date_due', 'status', 'percent_complete'], 'detail_available' => function() {return false;}, 'count' => 200, ]); return $result; } private function render_payments($parent_model, $parent_id) { $result = '
'; $result .= $this->listview_shortcode([ 'model' => 'Payment', 'parent_model' => $parent_model, 'link' => 'payments', 'parent_id' => $parent_id, 'no_restrictions' => true, 'title' => __('Payments', ONECRM_P_TEXTDOMAIN), 'fields' => ['full_number', 'payment_type', 'applied_amount', 'payment_date'], 'detail_available' => function() {return false;}, 'count' => 200, ]); return $result; } public function dashboard_create($model, $no_breadcrumbs = false) { $html = ''; $all = onecrm_p_get_all_modules(); $saved = get_option('onecrm_p_dashboard_config'); $module = onecrm_p_module_for_model($model); $modules = array_filter($saved, function($m) use($module) { return $m['enabled'] && $module === $m['module']; }); if (empty($modules)) { return $html; } $module = current($modules); $defs = array_filter($all, function($d) use ($module) { return $d['module'] === $module['module'];}); $def = current($defs); if (empty($def['can_create'])) { return $html; } if (!empty($def['partners_only']) && !$this->is_partner) { return $html; } $fields = array_map(function($f) {return $f['field'];}, array_filter($module['create'], function($f) { return $f['enabled'];}) ); $attr = [ 'model' => $model, 'fields' => $fields, 'columns' => 2, ]; $title = ''; if (!$no_breadcrumbs) { $title = '' . __("Dashboard", ONECRM_P_TEXTDOMAIN) . ' » '; } $title .= '' . htmlspecialchars($def['plural']) . ''; return "

{$title}

" . $this->onecrm_editor_start_shortcode(['action' => 'onecrm_p_model_create']) . $this->onecrm_fieldset_shortcode($attr) . $this->onecrm_editor_end_shortcode(['submit' => 'Save']) ; } public function dashboard_edit($model, $id, $no_breadcrumbs = false) { $html = ''; $all = onecrm_p_get_all_modules(true); $saved = get_option('onecrm_p_dashboard_config'); if ($model == 'Contact') { $module = onecrm_p_get_contacts_data(); $modules = [$module]; $no_list = true; } else { $module = onecrm_p_module_for_model($model); $modules = array_filter($saved, function($m) use($module) { return $m['enabled'] && $module === $m['module']; }); $no_list = false; } if (empty($modules)) { return $html; } $module = current($modules); $defs = array_filter($all, function($d) use ($module) { return $d['module'] === $module['module'];}); $def = current($defs); if (empty($def['can_create'])) { return $html; } if (!empty($def['partners_only']) && !$this->is_partner) { return $html; } $fields = array_map(function($f) {return $f['field'];}, array_filter($module['create'], function($f) { return $f['enabled'];}) ); if (empty($fields)) $fields = $module['create']['fixed_fields']; $attr = [ 'id' => $id, 'model' => $model, 'fields' => $fields, 'columns' => 2, ]; $title = ''; if (!$no_breadcrumbs) { $title = '' . __("Dashboard", ONECRM_P_TEXTDOMAIN) . ' » '; } if (! $no_list) { $title .= '' . htmlspecialchars($def['plural']) . ''; } else { $title .= htmlspecialchars($def['plural']); } $hidden = ''; if (!empty($_GET['return_to'])) { $hidden .= ''; } return "

{$title}

" . $this->onecrm_editor_start_shortcode(['action' => 'onecrm_p_model_save']) . $this->onecrm_fieldset_shortcode($attr) . $hidden . $this->onecrm_editor_end_shortcode(['submit' => 'Save']) ; } public function dashboard_personal_data($model, $id, $format, $no_breadcrumbs = false) { $html = ''; $all = onecrm_p_get_all_modules(true); $saved = get_option('onecrm_p_dashboard_config'); if ($model == 'Contact') { $module = onecrm_p_get_contacts_data(); $modules = [$module]; } else { $module = onecrm_p_module_for_model($model); $modules = array_filter($saved, function($m) use($module) { return $m['enabled'] && $module === $m['module']; }); } if (empty($modules)) return $html; $module = current($modules); $defs = array_filter($all, function($d) use ($module) { return $d['module'] === $module['module'];}); $def = current($defs); if (empty($def['personal_data'])) return $html; $title = ''; if (!$no_breadcrumbs) { $title .= '' . __("Dashboard", ONECRM_P_TEXTDOMAIN) . ' » '; } if($format == 'erase') { $label = 'Erase Personal Data'; $pdf = ''; } else { $label = 'Personal Data'; $pdf = $this->render_pdf_link($model, $id, true); } $title .= htmlspecialchars($def['plural']) .' » '. __($label, ONECRM_P_TEXTDOMAIN); $title .= $pdf; $attrs = [ 'model' => $model, 'id' => $id, 'fields' => $this->get_personal_data_fields($model), 'title_escaped' => true, 'title' => $title, 'detail_available' => false, 'columns' => 2, ]; if ($format == 'erase') { $attrs['render_type'] = 'erase_personal'; return $this->onecrm_editor_start_shortcode(['action' => 'onecrm_p_personal_data_erase']) . $this->detailview_shortcode($attrs, false) . $this->onecrm_editor_end_shortcode(['submit' => 'Erase']) ; } else { $dv = $this->detailview_shortcode($attrs, false); if ($dv === false) { return '
' . __('Customer Connection is not properly configured', ONECRM_P_TEXTDOMAIN) . '
' ; } return $dv; } } private function get_personal_data_fields($model) { $fields = []; if (!$client = $this->get_client()) return $fields; $model_obj = $client->model($model); try { $fields_meta = $model_obj->metadata(); } catch (\Exception $e) { return $fields; } if (empty($fields_meta['fields'])) return $fields; foreach ($fields_meta['fields'] as $field) { if (! empty($field['personal_info'])) $fields[] = $field['name']; } return $fields; } private function add_custom_fields($model_name, $fields, &$fields_meta, &$rows) { $single_row = false; if ($model_name == 'Invoice') { $paypal_link = get_option('onecrm_paypal_link'); if (in_array('paypal_button', $fields) && ! empty($paypal_link)) { if (! isset($fields_meta['paypal_button'])) $this->add_cutom_field_metadata($fields_meta, 'paypal_button'); if (! isset($rows[0])) { $rows = [$rows]; $single_row = true; } foreach ($rows as &$row) { if (! isset($row['amount_due']) || $row['amount_due'] <=0) continue; $item_name = $row['full_number'] .'-'. $row['name'] .' ('. $row['currency'] . $row['amount_due'] .')'; $item_name = urlencode($item_name); $link = $paypal_link . '&amount=' . $row['amount_due'] . '&item_name=' . $item_name; $row['paypal_button'] = 'PayPal'; } if ($single_row && isset($rows[0])) $rows = $rows[0]; } } } private function add_cutom_field_metadata(&$fields_meta, $field) { if ($field == 'paypal_button' && !isset($fields_meta['fields']['paypal_button'])) { $fields_meta['fields']['paypal_button'] = [ 'vname' => __('PayPal', ONECRM_P_TEXTDOMAIN), 'type' => 'html', 'name' => 'paypal_button' ]; } } function displayAuthErrors($errors) { if (in_array('login', $errors)) { $ret = '

'; $ret .= __('You need to login in order to access this area', ONECRM_P_TEXTDOMAIN); $ret .= '

'; $ret .= ''; $ret .= '