CVE-2025-55209
Description
contactmanager is a module for FreePBX@, which is an open source GUI that controls and manages Asterisk© (PBX). In versions 15.0.14 and below, 16.0.0 through 16.0.26.4 and 17.0.0 through 17.0.5, a stored cross-site scripting (XSS) vulnerability in FreePBX allows a low-privileged User Control Panel (UCP) user to inject malicious JavaScript into the system. The malicious code executes in the context of an administrator when they interact with the affected component, leading to session hijacking and potential privilege escalation. This issue is fixed in versions 15.0.14, 16.0.27 and 17.0.6.
Affected products
1- Range: release/12.0.0alpha2, release/12.0.0alpha3, release/12.0.0beta1, …
Patches
155abba0f1ab5finalize work on contact manager
9 files changed · +812 −60
Contactmanager.class.php+93 −39 modified@@ -352,10 +352,10 @@ public function getEntryByID($id) { 'e.id', 'e.groupid', 'e.user', - 'e.displayname', - 'e.fname', - 'e.lname', - 'e.title', + 'COALESCE(e.displayname,u.displayname,u.fname,u.username) as displayname', + 'COALESCE(e.fname,u.fname) as fname', + 'COALESCE(e.lname,u.lname) as lname', + 'COALESCE(e.title,u.title) as title', 'e.company', ); $sql = "SELECT " . implode(', ', $fields) . " FROM contactmanager_group_entries as e @@ -364,50 +364,86 @@ public function getEntryByID($id) { $sth->execute(array(':id' => $id)); $entry = $sth->fetch(\PDO::FETCH_ASSOC); - $numbers = $this->getNumbersByEntryID($id); - if ($numbers) { - foreach ($numbers as $number) { - $entry['numbers'][$number['id']] = array( - 'number' => $number['number'], - 'type' => $number['type'], - 'flags' => $number['flags'] ? explode('|', $number['flags']) : array(), - ); - } - } + $group = $this->getGroupByID($entry['groupid']); - $xmpps = $this->getXMPPsByEntryID($id); - if ($xmpps) { - foreach ($xmpps as $xmpp) { - $entry['xmpps'][$xmpp['id']] = array( - 'xmpp' => $xmpp['xmpp'], - ); - } - } + switch($group['type']) { + case "external": + $numbers = $this->getNumbersByEntryID($id); + if ($numbers) { + foreach ($numbers as $number) { + $entry['numbers'][$number['id']] = array( + 'number' => $number['number'], + 'type' => $number['type'], + 'flags' => $number['flags'] ? explode('|', $number['flags']) : array(), + ); + } + } - $emails = $this->getEmailsByEntryID($id); - if ($emails) { - foreach ($emails as $email) { - $entry['emails'][$email['id']] = array( - 'email' => $email['email'], - ); - } - } + $xmpps = $this->getXMPPsByEntryID($id); + if ($xmpps) { + foreach ($xmpps as $xmpp) { + $entry['xmpps'][$xmpp['id']] = array( + 'xmpp' => $xmpp['xmpp'], + ); + } + } - $websites = $this->getWebsitesByEntryID($id); - if ($websites) { - foreach ($websites as $website) { - $entry['websites'][$website['id']] = array( - 'website' => $website['website'], - ); - } - } + $emails = $this->getEmailsByEntryID($id); + if ($emails) { + foreach ($emails as $email) { + $entry['emails'][$email['id']] = array( + 'email' => $email['email'], + ); + } + } + $websites = $this->getWebsitesByEntryID($id); + if ($websites) { + foreach ($websites as $website) { + $entry['websites'][$website['id']] = array( + 'website' => $website['website'], + ); + } + } + break; + case "internal": + case "userman": + $user = \FreePBX::Userman()->getUserByID($entry['user']); + if(!empty($user['cell'])) { + $entry['numbers'][] = array( + 'number' => $user['cell'], + 'type' => 'cell', + 'flags' => array(), + ); + } + if(!empty($user['work'])) { + $entry['numbers'][] = array( + 'number' => $user['work'], + 'type' => 'work', + 'flags' => array(), + ); + } + if(!empty($user['home'])) { + $entry['numbers'][] = array( + 'number' => $user['home'], + 'type' => 'home', + 'flags' => array(), + ); + } + if(!empty($user['email'])) { + $entry['emails'][] = array( + 'email' => $user['email'] + ); + } + break; + } return $entry; } public function getEntriesByGroupID($groupid) { $fields = array( 'e.id', + 'e.id as uid', 'e.groupid', 'e.user', 'COALESCE(e.displayname,u.displayname,u.fname,u.username) as displayname', @@ -1000,12 +1036,13 @@ public function getContactsByUserID($id) { if(!empty($this->contactsCache)) { return $this->contactsCache; } + $umentries = $this->freepbx->Userman->getAllContactInfo(); $groups = $this->getGroupsByOwner($id); $contacts = array(); foreach($groups as $group) { switch($group['type']) { case "userman": - $entries = $this->freepbx->Userman->getAllContactInfo(); + $entries = $umentries; $final = array(); foreach($entries as $entry) { $entry['type'] = "userman"; @@ -1036,6 +1073,23 @@ public function getContactsByUserID($id) { $contacts = array_merge($contacts, $entries); break; case "internal": + $entries = $this->getEntriesByGroupID($group['id']); + $final = array(); + foreach($entries as $entry) { + foreach($umentries as $um) { + if($um['id'] == $entry['user']) { + $entry['type'] = "userman"; + //standardize all phone numbers, digits only + $entry['numbers'] = array( + 'cell' => preg_replace('/\D/','',$um['cell']), + 'work' => preg_replace('/\D/','',$um['work']), + 'home' => preg_replace('/\D/','',$um['home']), + ); + $final[] = $entry; + $contacts = array_merge($contacts, $entries); + } + } + } break; } }
ucp/assets/js/global.js+195 −3 modified@@ -9,13 +9,205 @@ var ContactmanagerC = UCPMC.extend({ }); }, poll: function(data) { - + var cm = this; + if (data.enabled) { + cm.contacts = data.contacts; + } }, display: function(event) { - + $(".add-additional").click(function(e) { + e.preventDefault(); + var type = $(this).data("type"); + $("." + type + " table").append("<tr>" + $("." + type + " .template").html() + "</tr>"); + }); + $("#addContact .additional").on("click", ".delete", function() { + $(this).parents("tr").remove(); + }); + $("#editContact .additional").on("click", ".delete", function() { + var table = $(this).parents("table"), count = 0, type = table.data("type"), data = [], id = $("#id").val(); + $(this).parents("tr").remove(); + table.find("tr").not(".template").find("input").each(function(i, v) { + var obj = {}; + obj[$(this).data("name")] = $(this).val(); + data.push(obj); + }); + $.post( "?quietmode=1&module=contactmanager&command=updatecontact", { id: id, key: type, value: data }, function( data ) { + if (data.status) { + $(".alert").text(data.message).addClass("alert-success").fadeIn("fast", function() { + $(this).delay(2000).fadeOut("fast"); + }); + } + }); + }); + $("#deletecontact").click(function(e) { + e.preventDefault(); + var id = $("#id").val(), groupid = $.url().param("group"); + if (confirm(_("Are you sure you want to delete this contact?"))) { + $("form input").prop("disabled", true); + $("#deletecontact").text(_("Deleting...")); + $("#deletecontact").prop("disabled", true); + $.post( "?quietmode=1&module=contactmanager&command=deletecontact", { id: id }, function( data ) { + if (data.status) { + $.pjax({ + url: "?display=dashboard&mod=contactmanager&view=group&id=" + groupid, + container: "#dashboard-content" + }); + } + }); + } + }); + $("#editContact input").not(".special").not(".specialn").blur(function(e) { + var key = $(this).prop("id"), value = $(this).val(), id = $("#id").val(); + $.post( "?quietmode=1&module=contactmanager&command=updatecontact", { id: id, key: key, value: value }, function( data ) { + if (data.status) { + $(".alert").text(data.message).addClass("alert-success").fadeIn("fast", function() { + $(this).delay(2000).fadeOut("fast"); + }); + } + }); + }); + $("#editContact .numbers").on("blur", "input", function(e) { + var table = $(this).parents("table"), count = 0, type = table.data("type"), data = [], id = $("#id").val(); + $(".numbers tr").filter(":visible").each(function(i, v) { + var obj = {}; + obj.number = $(this).find("input[data-name='number']").val(); + obj.type = $(this).find("select[data-name='type']").val(); + data.push(obj); + }); + $.post( "?quietmode=1&module=contactmanager&command=updatecontact", { id: id, key: type, value: data }, function( data ) { + if (data.status) { + $(".alert").text(data.message).addClass("alert-success").fadeIn("fast", function() { + $(this).delay(2000).fadeOut("fast"); + }); + } + }); + }); + $("#editContact .numbers").on("change", "select", function(e) { + var table = $(this).parents("table"), count = 0, type = table.data("type"), data = [], id = $("#id").val(); + $(".numbers tr").filter(":visible").each(function(i, v) { + var obj = {}; + obj.number = $(this).find("input[data-name='number']").val(); + obj.type = $(this).find("select[data-name='type']").val(); + data.push(obj); + }); + $.post( "?quietmode=1&module=contactmanager&command=updatecontact", { id: id, key: type, value: data }, function( data ) { + if (data.status) { + $(".alert").text(data.message).addClass("alert-success").fadeIn("fast", function() { + $(this).delay(2000).fadeOut("fast"); + }); + } + }); + }); + $("#editContact").on("blur", "input[class*='special']", function(e) { + var table = $(this).parents("table"), count = 0, type = table.data("type"), data = [], id = $("#id").val(); + table.find("tr").not(".template").find("input").each(function(i, v) { + var obj = {}; + obj[$(this).data("name")] = $(this).val(); + data.push(obj); + }); + $.post( "?quietmode=1&module=contactmanager&command=updatecontact", { id: id, key: type, value: data }, function( data ) { + if (data.status) { + $(".alert").text(data.message).addClass("alert-success").fadeIn("fast", function() { + $(this).delay(2000).fadeOut("fast"); + }); + } + }); + }); + $("#addcontact").click(function(e) { + e.preventDefault(); + var id = $.url().param("group"), contact = { numbers: [] }; + $("form input").not(".special").each(function(i, v) { + var item = $(v); + contact[item.prop("id")] = item.val(); + }); + $(".numbers tr").filter(":visible").each(function(i, v) { + var obj = {}; + obj.number = $(this).find("input[data-name='number']").val(); + obj.type = $(this).find("select[data-name='type']").val(); + contact.numbers.push(obj); + }); + $("form input").filter(":visible").filter(".special").each(function(i, v) { + var table = $(this).parents("table"), type = table.data("type"), data = []; + table.find("tr").not(".template").find("input").each(function(i, v) { + var obj = {}; + obj[$(this).data("name")] = $(this).val(); + data.push(obj); + }); + contact[type] = data; + }); + $("form input").prop("disabled", true); + $("#addcontact").text(_("Adding...")); + $("#addcontact").prop("disabled", true); + $.post( "?quietmode=1&module=contactmanager&command=addcontact", { id: id, contact: contact }, function( data ) { + if (data.status) { + $.pjax({ + url: "?display=dashboard&mod=contactmanager&view=group&id=" + id, + container: "#dashboard-content" + }); + } + }); + }); + $("#deletegroup").click(function(e) { + e.preventDefault(); + if (confirm(_("Are you sure you want to delete this group and all of it's contacts?"))) { + $.post( "?quietmode=1&module=contactmanager&command=deletegroup", { id: $(this).data("id") }, function( data ) { + if (data.status) { + $(".contact-group[data-name='" + data.name + "']").remove(); + $.pjax({ + url: "?display=dashboard&mod=contactmanager", + container: "#dashboard-content" + }); + } + }); + } + }); + $("#addgroup").click(function(e) { + var groupid = 1, groupname = $("#name").val(); + e.preventDefault(); + if ($(".contact-group[data-name='" + groupname + "']").length) { + alert(_("Group Already Exists")); + $("#name").focus(); + return false; + } + if (groupname === "") { + alert(_("Group Name Can Not Be Blank")); + $("#name").focus(); + return false; + } + $.post( "?quietmode=1&module=contactmanager&command=addgroup", { name: groupname }, function( data ) { + if (data.status) { + $(".contact-group:last").after('<div class="contact-group sub" data-name="' + groupname + '"><a cm-pjax href="?display=dashboard&mod=contactmanager&view=group&id=' + data.id + '" class="contact-group-inner">' + groupname + '<span class="badge">0</span></a></div>'); + $("#name").val(""); + $.pjax({ + url: "?display=dashboard&mod=contactmanager&view=group&id=" + data.id, + container: "#dashboard-content" + }); + } + }); + }); + $(".contact-item").click(function() { + $.pjax({ + url: "?display=dashboard&mod=contactmanager&view=contact&group=" + $(this).data("group") + "&id=" + $(this).data("contact"), + container: "#dashboard-content" + }); + }); + //clear old binds + $(document).off("click", "[cm-pjax] a, a[cm-pjax]"); + //then rebind! + if ($.support.pjax) { + $(document).on("click", "[cm-pjax] a, a[cm-pjax]", function(event) { + var container = $("#dashboard-content"); + $.pjax.click(event, { container: container }); + }); + } }, hide: function(event) { - + $(".contact-item").off("click"); + $("#addgroup").off("click"); + $("#deletegroup").off("click"); + $("#addcontact").off("click"); + $("#deletecontact").off("click"); + $("#editContact input").off("blur"); }, /** * Lookup a contact from the directory
ucp/assets/less/bootstrap.less+90 −0 modified@@ -1,7 +1,53 @@ #module-page-contactmanager { + .additional { + table { + width: 100%; + .template { + display: none; + } + tr td:first-child { + width: 30px; + i { + font-size: 150%; + cursor: pointer; + color: rgb(66, 139, 202); + } + } + } + } + .nav-container { + margin-bottom: 5px; + } + .group-container { + background-color: aliceblue; + border-radius: 5px; + padding: 10px; + width:80%; + @media only screen and (max-width : 991px) { + width:100%; + } + } + .contact-container { + label { + text-decoration: underline; + } + ul { + margin-bottom: 0px; + } + background-color: aliceblue; + border-radius: 5px; + padding: 10px; + width:80%; + @media only screen and (max-width : 991px) { + width:100%; + } + } .separator-list { height: 15px; } + .contact-group { + background-color: #fff; + } .contact-group-list { background-color: aliceblue; } @@ -10,6 +56,7 @@ border-radius: 2px; a { color: #ffffff; + background-color: #428bca; } a:hover { text-decoration: none; @@ -49,4 +96,47 @@ color: #2a6496; } } + .contact-table { + font-size: 90%; + margin-bottom: inherit; + .contact-header { + th:not(.noclick) { + i { + float: right; + } + cursor: pointer; + } + th:not(.noclick):hover { + background-color:#f5f5f5; + } + background-color: aliceblue; + } + .contact-item { + td { + cursor: pointer; + } + color: black; + background-color: rgba(255, 255, 255, 0.5); + .actions { + a { + color: black; + text-decoration: none; + cursor: pointer; + } + i { + color:black; + } + } + i{ + font-size: large; + color: grey; + } + i.out { + color: #7a16af; + } + i.in { + color: #0fc61b; + } + } + } }
ucp/Contactmanager.class.php+158 −11 modified@@ -11,56 +11,191 @@ class Contactmanager extends Modules{ protected $module = 'Contactmanager'; private $ext = 0; - function __construct($Modules) { + public function __construct($Modules) { $this->Modules = $Modules; $this->cm = $this->UCP->FreePBX->Contactmanager; $this->user = $this->UCP->User->getUser(); } - public function lookupMultiple($search) { - $entry = $this->cm->lookupMultipleByUserID($this->user['id'],$search); - return $entry; + /** + * Determine what commands are allowed + * + * Used by Ajax Class to determine what commands are allowed by this class + * + * @param string $command The command something is trying to perform + * @param string $settings The Settings being passed through $_POST or $_PUT + * @return bool True if pass + */ + function ajaxRequest($command, $settings) { + switch($command) { + case 'updatecontact': + case 'deletecontact': + case 'addcontact': + case 'deletegroup': + case 'addgroup': + return true; + default: + return false; + break; + } } - public function lookup($search) { - $entry = $this->cm->lookupByUserID($this->user['id'],$search); - return $entry; + /** + * The Handler for all ajax events releated to this class + * + * Used by Ajax Class to process commands + * + * @return mixed Output if success, otherwise false will generate a 500 error serverside + */ + function ajaxHandler() { + $return = array("status" => false, "message" => ""); + switch($_REQUEST['command']) { + case 'updatecontact': + $entry = $this->cm->getEntryByID($_REQUEST['id']); + if(!empty($entry)) { + $entry[$_REQUEST['key']] = $_REQUEST['value']; + $return = $this->cm->updateEntry($_REQUEST['id'], $entry); + break; + } + $return = array("status" => false, "message" => _("Unauthorized")); + break; + case 'deletecontact': + $entry = $this->cm->getEntryByID($_REQUEST['id']); + if(!empty($entry)) { + $g = $this->cm->getGroupByID($entry['groupid']); + if($g['owner'] == $this->user['id']) { + $return = $this->cm->deleteEntryByID($_REQUEST['id']); + break; + } + } + $return = array("status" => false, "message" => _("Unauthorized")); + break; + case 'addcontact': + $g = $this->cm->getGroupByID($_REQUEST['id']); + if($g['owner'] == $this->user['id']) { + $contact = $_REQUEST['contact']; + $contact['user'] = -1; + $return = $this->cm->addEntryByGroupID($_REQUEST['id'], $contact); + } else { + $return = array("status" => false, "message" => _("Unauthorized")); + } + break; + case 'deletegroup': + $g = $this->cm->getGroupByID($_REQUEST['id']); + if($g['owner'] == $this->user['id']) { + $return = $this->cm->deleteGroupByID($_REQUEST['id']); + $return['name'] = $g['name']; + } else { + $return = array("status" => false, "message" => _("Unauthorized")); + } + break; + case 'addgroup': + $return = $this->cm->addGroup($_POST['name'], 'external', $this->user['id']); + break; + default: + return false; + break; + } + return $return; } /** * Generate the display in UCP */ - function getDisplay() { + public function getDisplay() { $view = !empty($_REQUEST['view']) ? $_REQUEST['view'] : ''; + $displayvars = array(); $displayvars['groups'] = $this->cm->getGroupsByOwner($this->user['id']); $displayvars['activeList'] = "mycontacts"; $displayvars['total'] = 0; + $displayvars['orderby'] = 'displayname'; + $displayvars['order'] = 'desc'; + $displayvars['readonly'] = true; + $displayvars['add'] = false; $allContacts = array(); $c = 1; foreach($displayvars['groups'] as &$group) { + $group['readonly'] = ($group['owner'] == -1); $group['contacts'] = $this->cm->getEntriesByGroupID($group['id']); $group['count'] = count($group['contacts']); $displayvars['total'] = $displayvars['total'] + $group['count']; $allContacts = array_merge($allContacts,$group['contacts']); + if(!empty($_REQUEST['view']) && $_REQUEST['view'] == "group" && $_REQUEST['id'] == $group['id']) { + $displayvars['activeList'] = $group['name']; + $displayvars['contacts'] = $group['contacts']; + $displayvars['readonly'] = $group['readonly']; + }else if(!empty($_REQUEST['view']) && $_REQUEST['view'] == "contact" && $_REQUEST['group'] == $group['id']) { + $displayvars['activeList'] = $group['name']; + $displayvars['readonly'] = $group['readonly']; + } } usort($allContacts, function($a, $b) { return strnatcmp($a['displayname'], $b['displayname']); }); switch($view) { + case "addcontact": + $g = $this->cm->getGroupByID($_REQUEST['group']); + if(!empty($g)) { + if($g['owner'] != -1) { + $displayvars['activeList'] = $g['name']; + $displayvars['add'] = true; + $mainDisplay = $this->load_view(__DIR__.'/views/contact.php',$displayvars); + break; + } + } + $displayvars['activeList'] = ''; + $mainDisplay = _("Not Authorized"); + break; + case "addgroup": + $displayvars['activeList'] = "addgroup"; + $mainDisplay = $this->load_view(__DIR__.'/views/groupcreate.php',$displayvars); + break; + case "contact": + $g = $this->cm->getGroupByID($_REQUEST['group']); + if(!empty($g)) { + $displayvars['contact'] = $this->cm->getEntryByID($_REQUEST['id']); + if($g['owner'] == -1) { + $mainDisplay = $this->load_view(__DIR__.'/views/contactro.php',$displayvars); + } else { + $mainDisplay = $this->load_view(__DIR__.'/views/contact.php',$displayvars); + } + } else { + $mainDisplay = _("Not Authorized"); + } + break; default: - $displayvars['contacts'] = $allContacts; + if($_REQUEST['view'] == "group" && isset($_REQUEST['id'])) { + $g = $this->cm->getGroupByID($_REQUEST['id']); + if($g['owner'] == -1 || $g['owner'] == $this->user['id']) { + $displayvars['contacts'] = $displayvars['contacts']; + $mainDisplay = $this->load_view(__DIR__.'/views/contacts.php',$displayvars); + break; + } + } + $displayvars['contacts'] = !empty($displayvars['contacts']) ? $displayvars['contacts'] : $allContacts; + $mainDisplay = $this->load_view(__DIR__.'/views/contacts.php',$displayvars); break; } - $mainDisplay = $this->load_view(__DIR__.'/views/contacts.php',$displayvars); + $html = $this->load_view(__DIR__.'/views/nav.php',$displayvars); $html .= $mainDisplay; return $html; } + public function lookupMultiple($search) { + $entry = $this->cm->lookupMultipleByUserID($this->user['id'],$search); + return $entry; + } + + public function lookup($search) { + $entry = $this->cm->lookupByUserID($this->user['id'],$search); + return $entry; + } + /** * Setup Menu Items for display in UCP */ @@ -72,10 +207,22 @@ public function getMenuItems() { return $menu; } + public function poll() { + $contacts = $this->cm->getContactsByUserID($this->user['id']); + if(!empty($contacts)) { + return array( + 'enabled' => true, + 'contacts' => $contacts + ); + } else { + return array('enabled' => false); + } + } + /** * Send settings to UCP upon initalization */ - function getStaticSettings() { + public function getStaticSettings() { $contacts = $this->cm->getContactsByUserID($this->user['id']); if(!empty($contacts)) { return array(
ucp/views/contact.php+178 −0 added@@ -0,0 +1,178 @@ +<div class="col-md-10"> + <div class="contact-container"> + <div class="alert" role="alert" style="display:none"></div> + <form role="form" id="<?php echo ($add) ? 'add' : 'edit'?>Contact"> + <div class="form-group"> + <label for="displayname">Display Name</label> + <input type="text" class="form-control" id="displayname" placeholder="Display Name" value="<?php echo $contact['displayname']?>"> + </div> + <div class="form-group"> + <label for="fname">First Name</label> + <input type="text" class="form-control" id="fname" placeholder="First Name" value="<?php echo $contact['fname']?>"> + </div> + <div class="form-group"> + <label for="lastname">Last Name</label> + <input type="text" class="form-control" id="lname" placeholder="Last Name" value="<?php echo $contact['lname']?>"> + </div> + <div class="form-group"> + <label for="title">Title</label> + <input type="text" class="form-control" id="title" placeholder="Title" value="<?php echo $contact['title']?>"> + </div> + <div class="form-group"> + <label for="company">Company</label> + <input type="text" class="form-control" id="company" placeholder="Company" value="<?php echo $contact['company']?>"> + </div> + <div class="form-group"> + <label>Numbers</label> + <div class="numbers additional"> + <table data-type="numbers"> + <tr class="template"> + <td> + <a><i class="fa fa-ban fa-fw"></i></a> + </td> + <td> + <input type="text" class="form-control number" data-name="number" value=""> + </td> + <td> + <select class="form-control number" data-name="type"> + <option value="work" selected="selected">Work</option> + <option value="home">Home</option> + <option value="cell">Cell</option> + <option value="other">Other</option> + </select> + </td> + <td style="text-align: right;"> + <label for="dndenable"><?php echo _('SMS')?>:</label> + </td> + <td> + <div class="onoffswitch"> + <input type="checkbox" name="dndenable" data-name="flag" class="onoffswitch-checkbox number" id="dndenable" <?php echo ($enabled) ? 'checked' : ''?>> + <label class="onoffswitch-label" for="dndenable"> + <div class="onoffswitch-inner"></div> + <div class="onoffswitch-switch"></div> + </label> + </div> + </td> + <td style="text-align: right;"> + <label for="dndenable"><?php echo _('Fax')?>:</label> + </td> + <td> + <div class="onoffswitch"> + <input type="checkbox" name="dndenable" data-name="flag" class="onoffswitch-checkbox number" id="fax" <?php echo ($enabled) ? 'checked' : ''?>> + <label class="onoffswitch-label" for="fax"> + <div class="onoffswitch-inner"></div> + <div class="onoffswitch-switch"></div> + </label> + </div> + </td> + </tr> + <?php foreach($contact['numbers'] as $number) {?> + <tr> + <td> + <a><i class="fa fa-ban fa-fw"></i></a> + </td> + <td> + <input type="text" class="form-control number" data-name="number" value="<?php echo $number['number']?>"> + </td> + <td> + <select class="form-control number" data-name="type"> + <option value="work" <?php echo ($number['type'] == "work") ? 'selected' : ''?>>Work</option> + <option value="home" <?php echo ($number['type'] == "home") ? 'selected' : ''?>>Home</option> + <option value="cell" <?php echo ($number['type'] == "cell") ? 'selected' : ''?>>Cell</option> + <option value="other" <?php echo ($number['type'] == "other") ? 'selected' : ''?>>Other</option> + </select> + </td> + </tr> + <?php } ?> + </table> + </div> + <button class="btn btn-default btn-xs add-additional" data-type="numbers"><i class="fa fa-plus fa-fw"></i>Add Number</button> + </div> + <div class="form-group"> + <label>XMPP</label> + <div class="xmpps additional"> + <table data-type="xmpps"> + <tr class="template"> + <td> + <i class="fa fa-ban fa-fw delete"></i> + </td> + <td> + <input type="text" class="form-control special" data-name="xmpp"> + </td> + </tr> + <?php foreach($contact['xmpps'] as $xmpp) {?> + <tr> + <td> + <i class="fa fa-ban fa-fw delete"></i> + </td> + <td> + <input type="text" class="form-control special" data-name="xmpp" value="<?php echo $xmpp['xmpp']?>"> + </td> + </tr> + <?php } ?> + </table> + </div> + <button class="btn btn-default btn-xs add-additional" data-type="xmpps"><i class="fa fa-plus fa-fw"></i>Add XMPP</button> + </div> + <div class="form-group"> + <label>Email</label> + <div class="emails additional"> + <table data-type="emails"> + <tr class="template"> + <td> + <i class="fa fa-ban fa-fw delete"></i> + </td> + <td> + <input type="text" class="form-control special" data-name="email"> + </td> + </tr> + <?php foreach($contact['emails'] as $email) {?> + <tr> + <td> + <i class="fa fa-ban fa-fw delete"></i> + </td> + <td> + <input type="text" class="form-control special" data-name="email" value="<?php echo $email['email']?>"> + </td> + </tr> + <?php } ?> + </table> + </div> + <button class="btn btn-default btn-xs add-additional" data-type="emails"><i class="fa fa-plus fa-fw"></i>Add Email</button> + </div> + <div class="form-group"> + <label>Website</label> + <div class="websites additional"> + <table data-type="websites"> + <tr class="template"> + <td> + <i class="fa fa-ban fa-fw delete"></i> + </td> + <td> + <input type="text" class="form-control special" data-name="website"> + </td> + </tr> + <?php foreach($contact['websites'] as $website) {?> + <tr> + <td> + <i class="fa fa-ban fa-fw delete"></i> + </td> + <td> + <input type="text" class="form-control special" data-name="website" value="<?php echo $website['website']?>"> + </td> + </tr> + <?php } ?> + </table> + </div> + <button class="btn btn-default btn-xs add-additional" data-type="websites"><i class="fa fa-plus fa-fw"></i>Add Website</button> + </div> + <input type="hidden" id="mode" name="mode" value="<?php echo ($add) ? 'add' : 'edit'?>"> + <?php if($add) {?> + <button id="addcontact" class="btn btn-default">Add Contact</button> + <?php } else { ?> + <input type="hidden" id="id" name="id" value="<?php echo $contact['id']?>"> + <button id="deletecontact" class="btn btn-default"><i class="fa fa-trash-o"></i> Delete Contact</button> + <?php } ?> + </form> + </div> +</div>
ucp/views/contactro.php+74 −0 added@@ -0,0 +1,74 @@ +<div class="col-md-10"> + <div class="contact-container"> + <form role="form"> + <div class="form-group"> + <label>Display Name</label><br/> + <?php echo $contact['displayname']?> + </div> + <div class="form-group"> + <label>First Name</label><br/> + <?php echo $contact['fname']?> + </div> + <div class="form-group"> + <label>Last Name</label><br/> + <?php echo $contact['lname']?> + </div> + <?php if(!empty($contact['title'])) {?> + <div class="form-group"> + <label>Title</label><br/> + <?php echo $contact['title']?> + </div> + <?php } ?> + <?php if(!empty($contact['company'])) {?> + <div class="form-group"> + <label>Company</label><br/> + <?php echo $contact['company']?> + </div> + <?php } ?> + <?php if(!empty($contact['numbers'])) {?> + <div class="form-group"> + <label>Numbers</label><br/> + <ul> + <?php foreach($contact['numbers'] as $number) {?> + <li data-type="number" data-flag='<?php echo json_encode($number['flags'])?>'><?php echo $number['type']?>: <?php echo $number['number']?> + <?php foreach($number['flags'] as $flag) {?> + (<?php echo $flag?>) + <?php } ?> + </li> + <?php } ?> + </ul> + </div> + <?php } ?> + <?php if(!empty($contact['xmpps'])) {?> + <div class="form-group"> + <label>Xmpp</label><br/> + <ul> + <?php foreach($contact['xmpps'] as $number) {?> + <li data-type="xmpp"><?php echo $number['xmpp']?></li> + <?php } ?> + </ul> + </div> + <?php } ?> + <?php if(!empty($contact['emails'])) {?> + <div class="form-group"> + <label>Emails</label><br/> + <ul> + <?php foreach($contact['emails'] as $number) {?> + <li data-type="email"><?php echo $number['email']?></li> + <?php } ?> + </ul> + </div> + <?php } ?> + <?php if(!empty($contact['websites'])) {?> + <div class="form-group"> + <label>Website</label><br/> + <ul> + <?php foreach($contact['websites'] as $number) {?> + <li data-type="website"><a href="<?php echo $number['website']?>" target="_blank"><?php echo $number['website']?></a></li> + <?php } ?> + </ul> + </div> + <?php } ?> + </form> + </div> +</div>
ucp/views/contacts.php+9 −5 modified@@ -1,5 +1,5 @@ <div class="col-md-10"> - <div class="row"> + <div class="row nav-container"> <div class="col-sm-8"> <?php echo $pagnation;?> </div> @@ -12,17 +12,21 @@ </div> </div> </div> + <?php if(isset($readonly) && !$readonly) {?> + <a cm-pjax href="?display=dashboard&mod=contactmanager&view=addcontact&group=<?php echo $_REQUEST['id']?>" class="btn btn-default btn-sm"><i class="fa fa-plus"></i> Add New Contact</a> + <button id="deletegroup" class="btn btn-default btn-sm pull-right" data-id="<?php echo $_REQUEST['id']?>"><i class="fa fa-trash-o"></i> Delete This Group</button> + <?php } ?> <div class="table-responsive"> - <table class="table table-hover table-bordered cdr-table"> - <thead> + <table class="table table-hover table-bordered contact-table"> + <thead class="contact-header"> <tr> - <th><?php echo _('Display Name')?></th> + <th><?php echo _('Display Name')?><i class="fa fa-chevron-<?php echo ($order == 'desc' && $orderby == 'displayname') ? 'down' : 'up'?> <?php echo ($orderby == 'displayname') ? '' : 'hidden'?>"></i></th> <th><?php echo _('First Name')?></th> <th><?php echo _('Last Name')?></th> </tr> </thead> <?php foreach($contacts as $contact) {?> - <tr> + <tr class="contact-item" data-group="<?php echo $contact['groupid']?>" data-contact="<?php echo $contact['uid']?>"> <td><?php echo $contact['displayname'];?></td> <td><?php echo $contact['fname'];?></td> <td><?php echo $contact['lname'];?></td>
ucp/views/groupcreate.php+11 −0 added@@ -0,0 +1,11 @@ +<div class="col-md-10"> + <div class="group-container"> + <form role="form"> + <div class="form-group"> + <label for="name">Group Name</label><br/> + <input id="name" class="form-control" type="text" placeholder="Group Name"> + </div> + <button id="addgroup" class="btn btn-default">Add Group</button> + </form> + </div> +</div>
ucp/views/nav.php+4 −2 modified@@ -1,8 +1,10 @@ <div class="col-md-2"> <div class="contact-group-list"> - <div class="contact-group <?php echo ('mycontacts' == $activeList) ? 'active' : ''?>"><a vm-pjax href="?display=dashboard&mod=faxpro&view=send" class="contact-group-inner"><?php echo _('My Contacts')?><span class="badge"><?php echo $total?></span></a></div> + <div class="contact-group <?php echo ('mycontacts' == $activeList) ? 'active' : ''?>"><a cm-pjax href="?display=dashboard&mod=contactmanager&view=all" class="contact-group-inner"><?php echo _('My Contacts')?><span class="badge"><?php echo $total?></span></a></div> + <div class="contact-group sub <?php echo ('addgroup' == $activeList) ? 'active' : ''?>"><a cm-pjax href="?display=dashboard&mod=contactmanager&view=addgroup" class="contact-group-inner">Add Group<i class="fa fa-plus"></i></a></div> <?php foreach($groups as $group) { ?> - <div class="contact-group sub <?php echo ($group['name'] == $activeList) ? 'active' : ''?>"><a vm-pjax href="?display=dashboard&mod=faxpro&view=send" class="contact-group-inner"><?php echo $group['name']?><span class="badge"><?php echo $group['count']?></span></a></div> + <div class="contact-group sub <?php echo ($group['name'] == $activeList) ? 'active' : ''?>" data-name="<?php echo $group['name']?>"><a cm-pjax href="?display=dashboard&mod=contactmanager&view=group&id=<?php echo $group['id']?>" class="contact-group-inner"><?php echo $group['name']?><span class="badge"><?php echo $group['count']?></span></a></div> <?php } ?> </div> + <br/> </div>
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
2News mentions
0No linked articles in our index yet.