Find here a set of questions and answers about developing and fine tuning the application from within
You have to force the state in DetailView and CallRelatedList Single Pane forced on module patch
--- a/CallRelatedList.php
+++ b/CallRelatedList.php
@@ -16,7 +16,7 @@
$action = vtlib_purify($_REQUEST['action']);
$record = vtlib_purify($_REQUEST['record']);
$isduplicate = vtlib_purify($_REQUEST['isDuplicate']);
-
+$singlepane_view = 'true';
if($singlepane_view == 'true' && $action == 'CallRelatedList') {
header("Location:index.php?action=DetailView&module=$currentModule&record=$record&parenttab=$category");
} else {
--- a/DetailView.php
+++ b/DetailView.php
@@ -71,6 +71,7 @@
}
$smarty->assign('IS_REL_LIST', isPresentRelatedLists($currentModule));
+$singlepane_view = 'true';
$smarty->assign('SinglePane_View', $singlepane_view);
if($singlepane_view == 'true') {
Look for this field meta data in the database table vtiger_field and change the value of the column generatedtype to 2.
with global variables.
The next code adds a substring function and should suffice to show how to add more funtions. Please share if you create any.
Index: modules/com_vtiger_workflow/expression_engine/VTExpressionsManager.inc
===================================================================
--- modules/com_vtiger_workflow/expression_engine/VTExpressionsManager.inc (revisión: 938)
+++ modules/com_vtiger_workflow/expression_engine/VTExpressionsManager.inc (revisión: 939)
@@ -41,7 +41,7 @@
function expressionFunctions() {
return array('concat' => 'concat(a,b)', 'time_diffdays' => 'time_diffdays(a,b)', 'time_diff' => 'time_diff(a,b)',
'add_days' => 'add_days(datefield, noofdays)', 'sub_days' => 'sub_days(datefield, noofdays)',
- 'get_date' => "get_date('today')");
+ 'get_date' => "get_date('today')", 'substring' => "substring(stringfield,start,end)");
}
}
Index: modules/com_vtiger_workflow/expression_engine/VTExpressionEvaluater.inc
===================================================================
--- modules/com_vtiger_workflow/expression_engine/VTExpressionEvaluater.inc (revisión: 938)
+++ modules/com_vtiger_workflow/expression_engine/VTExpressionEvaluater.inc (revisión: 939)
@@ -144,6 +144,15 @@
}
}
+function __vt_substring($arr) {
+ if (count($arr)<2 or count($arr)>3) return $arr[0];
+ if (count($arr)==2) {
+ return substr($arr[0],$arr[1]);
+ } else {
+ return substr($arr[0],$arr[1],$arr[2]);
+ }
+}
+
/** END * */
class VTFieldExpressionEvaluater{
function __construct($expr){
@@ -165,7 +174,8 @@
'time_diffdays' => '__vt_time_diffdays',
'add_days' => '__vt_add_days',
'sub_days' => '__vt_sub_days',
- 'get_date' => '__vt_get_date'
+ 'get_date' => '__vt_get_date',
+ 'substring' => '__vt_substring'
);
$this->operations = array_merge($this->functions, $this->operators);
You have to apply this next code change.
diff --git a/include/js/general.js b/include/js/general.js
index d79cc33..c5eae5d 100755
--- a/include/js/general.js
+++ b/include/js/general.js
@@ -4066,6 +4066,7 @@ function submitFormForActionWithConfirmation(formName, action, confirmationMsg)
function submitFormForAction(formName, action) {
var form = document.forms[formName];
if (!form) return false;
+ form.target="_blank";
form.action.value = action;
form.submit();
return true;
See this page for tips on playing with PDF output: PDF Formatting
This patch accomplishes this functionality. You don't have to collect the currency symbol, just save the value and format like with the php function "number_format" to get the value with the correct decimals.
Index: modules/Reports/ReportUtils.php
===================================================================
--- modules/Reports/ReportUtils.php (revisión: 5892)
+++ modules/Reports/ReportUtils.php (revisión: 5964)
@@ -75,11 +75,10 @@
$cur_sym_rate = getCurrencySymbolandCRate($currency_id);
if($value!='') {
$formattedCurrencyValue = CurrencyField::convertToUserFormat($currency_value, null, true);
- $fieldvalue = CurrencyField::appendCurrencySymbol($formattedCurrencyValue, $cur_sym_rate['symbol']);
+ $fieldvalue = number_format($formattedCurrencyValue,2,'.','');
}
} else {
- $currencyField = new CurrencyField($value);
- $fieldvalue = $currencyField->getDisplayValue();
+ $fieldvalue = number_format($value,2,'.','');
}
} elseif ($dbField->name == "PurchaseOrder_Currency" || $dbField->name == "SalesOrder_Currency"
Each module has defined it's default column for sorting. This must be changed in the code in the module's main script. For example, for Accounts we can find:
//Added these variables which are used as default order by and sortorder in ListView
var $default_order_by = 'accountname';
var $default_sort_order = 'ASC';
where we can change the variable $default_order_by to any valid column of the account
Go to the database and change the value of vtiger_field.presence to
This is a very useful trick when we lose the user_privileges directory or the admin user files and can't login.
cd <application root directory>
php -r "require_once 'include/utils/UserInfoUtil.php'; RecalculateSharingRules();"
This should work although I have my doubts about this functionality because I understand that you may want to associate the closed won opportunity to the quote. NOTE that the patch affects Quotes and SalesOrders.
diff --git a/Popup.php b/Popup.php
index 16e7048..c6faf32 100644
--- a/Popup.php
+++ b/Popup.php
@@ -237,6 +237,9 @@ else
$url_string .='&recordid='.vtlib_purify($_REQUEST['recordid']);
$where_relquery = getRelCheckquery($currentModule,$_REQUEST['return_module'],$_REQUEST['recordid']);
}
+ if ($currentModule=='Potentials' and (isset($_REQUEST['restrictclosed']) and $_REQUEST['restrictclosed']=='1')) {
+ $where_relquery = " and sales_stage not like 'Closed%' ";
+ }
if(isset($_REQUEST['relmod_id']) || isset($_REQUEST['fromPotential']))
{
if($_REQUEST['relmod_id'] !='')
diff --git a/include/js/general.js b/include/js/general.js
index d0feb0a..72db333 100755
--- a/include/js/general.js
+++ b/include/js/general.js
@@ -2379,9 +2379,9 @@ function selectPotential()
parent_module = 'Contacts';
}
if(record_id != '')
- window.open("index.php?module=Potentials&action=Popup&html=Popup_picker&popuptype=specific_potential_account_address&form=EditView&relmod_id="+record_id+"&parent_module="+parent_module,"test","width=640,height=602,resizable=0,scrollbars=0");
+ window.open("index.php?module=Potentials&action=Popup&html=Popup_picker&popuptype=specific_potential_account_address&form=EditView&restrictclosed=1&relmod_id="+record_id+"&parent_module="+parent_module,"test","width=640,height=602,resizable=0,scrollbars=0");
else
- window.open("index.php?module=Potentials&action=Popup&html=Popup_picker&popuptype=specific_potential_account_address&form=EditView","test","width=640,height=602,resizable=0,scrollbars=0");
+ window.open("index.php?module=Potentials&action=Popup&html=Popup_picker&popuptype=specific_potential_account_address&form=EditView&restrictclosed=1","test","width=640,height=602,resizable=0,scrollbars=0");
}
//to select Quote Popup
function selectQuote()
More or less like this:
This is very easy with vtlib. Simply get an instance of the field you want to modify and use the setRelatedModules() method. Have a look at the code below:
<?php
// Turn on debugging level
$Vtiger_Utils_Log = true;
include_once('vtlib/Vtiger/Module.php');
$modname = 'Potentials';
$module = Vtiger_Module::getInstance($modname);
if($module) {
$field = Vtiger_Field::getInstance('related_to',$module);
$field->setRelatedModules(Array('Invoice'));
echo "<br><b>Modified Field on $modname module.</b><br>";
} else {
echo "<b>Failed to find $modname module.</b><br>";
}
?>
You have to do this from database.
Use the Debug_Report_Query Global variable
Use the Debug_ListView_Query Global variable
For bulk operation script setting $VTIGER_BULK_SAVE_MODE to true - turns off the Evening API.
The import module set variable $VTIGER_BULK_SAVE_MODE = true , to not load the events to execute workflows. When you need to execute workflows when import records, you can apply the next patch. This patch force the validation to isBulkSaveMode, with this the workflows always will be load in any part of code.
After your importation, remember to undo the patch.
diff --git a/data/CRMEntity.php b/data/CRMEntity.php
index 30b7c2e..c737722 100755
--- a/data/CRMEntity.php
+++ b/data/CRMEntity.php
@@ -48,6 +48,7 @@ class CRMEntity {
* to improve performance.
*/
static function isBulkSaveMode() {
+ return false;
global $VTIGER_BULK_SAVE_MODE;
if (isset($VTIGER_BULK_SAVE_MODE) && $VTIGER_BULK_SAVE_MODE) {
return true;
Instead of calling save(), use saveentity(), that will not trigger the workflows again.
I would suggest that you add your function and libraries in the event/custom code that you create. For example, suppose that you need to include some google drive library and some new functions to the save event of a document. You decide to add a vtiger.aftersave event. In the file where your new functionality resides you will add the library include at the beginning and your functions inside the new class so you don't need to modify the common utils base code. Something like this:
<?php
require_once('google drive library');
class YourCustomEventHandlerClass extends VTEventHandler {
function YourNewFunction() {
...
}
function handleEvent($eventName, $entityData) {
global $log, $adb;
...
$this->YourNewFunction();
...
}
}
?>
It seems that your database can't setup the foreign key constraint for some reason. The correct path would be to review your data and make sure it is correct. In other words, the SQL must work, if it doesn't there is some inconsistency in your FAQ table. As an extreme case you can eliminate the constraint, the program will work the same and you will have custom field support on the FAQ module, simply the information in those custom fields will not be deleted when you eliminate a FAQ. Here is the change:
diff --git a/build/changeSets/cffaq.php b/build/changeSets/cffaq.php
index 3a10292..cf4896a 100644
--- a/build/changeSets/cffaq.php
+++ b/build/changeSets/cffaq.php
@@ -23,8 +23,7 @@ class cffaq extends cbupdaterWorker {
} else {
$this->ExecuteQuery("CREATE TABLE IF NOT EXISTS vtiger_faqcf (
faqid int(19),
- PRIMARY KEY (faqid),
- CONSTRAINT fk_1_vtiger_faqcf FOREIGN KEY (faqid) REFERENCES vtiger_faq(id) ON DELETE CASCADE
+ PRIMARY KEY (faqid)
) ENGINE=InnoDB DEFAULT CHARSET=utf8", array());
$this->sendMsg('Changeset '.get_class($this).' applied!');
$this->markApplied();
I encountered a race condition using the 2-steps login process provided by vtiger's webservices. Say that you have a multi-process / multi-threaded application using vtiger's webservices. Each thread/process has to call getchallenge+login before proceeding in doing what they have to do (that's what doLogin() in vtwsclib does).
The problem lies in the getchallenge phase. include/Webservices/AuthToken.php does
delete from vtiger_ws_userauthtoken where userid=?
for the current user. This instruction can be executed in between the 2 getchallenge+login steps of another webservice thread/process, therefore destroying its chance of doing a correct login.
Maybe the solution would be for the getchallenge step to detect if there is already an existing (not expired) outstanding getchallenge request for the current user and return that.
Index: trunk/include/Webservices/AuthToken.php
===================================================================
--- trunk/include/Webservices/AuthToken.php (revision 7)
+++ trunk/include/Webservices/AuthToken.php (working copy)
@@ -19,6 +19,14 @@
$servertime = time();
$expireTime = time()+(60*5);
+ $sql = "select userid,token,expiretime, count(*) as count from vtiger_ws_userauthtoken group by userid=?";
+ $res = $adb->pquery($sql,array($userid,$servertime));
+ $result = $adb->fetchByAssoc($res);
+ if ($result['count']>=1) {
+ return array("token"=>$result["token"],"serverTime"=>$servertime,"expireTime"=>$result["expiretime"]);
+ }
+
+
$sql = "delete from vtiger_ws_userauthtoken where userid=?";
$adb->pquery($sql,array($userid));
The following URL format will suppress the header and footer information.
index.php?module=MyModule&action=MyModuleAjax&file=MyAction&ajax=true&...
Add this code to Save.php of the Invoice
$source_id = $_REQUEST['return_id'];
unset($_REQUEST['return_id']);
$source_module = $_REQUEST['return_module'];
$_REQUEST['return_module'] = 'Invoice';
$_REQUEST['return_action'] = 'DetailView';
so it ends up looking like this:
<?php
$source_id = $_REQUEST['return_id'];
unset($_REQUEST['return_id']);
$source_module = $_REQUEST['return_module'];
$_REQUEST['return_module'] = 'Invoice';
$_REQUEST['return_action'] = 'DetailView';
require_once('modules/Vtiger/Save.php');
?>
Thanks Luke :-)
As long as the field is mapped with target-table or column through meta-data and follows the Entity module convention system would continue to work.
Shared table poses a challenge when different module records enable edits of fields.
It would also come in the way of performance (specifically query cache invalidation).
If modules are from different publishers then sharing table is not a good idea, as the control or convention can be too tedious to maintain/manage.
Go to the Business Actions module and add a LISTVIEWBASIC button with this action:
massPrint(gVTModule)
PLEASE be careful with the update SQL commands. Test them thoroughly
SELECT FROM_UNIXTIME(`lastsavedtime`),`lastsavedtime`, createdtime, modifiedtime
FROM `vtiger_mailmanager_mailattachments`
inner join vtiger_crmentity on crmid = attachid;
UPDATE vtiger_crmentity
INNER JOIN `vtiger_mailmanager_mailattachments` ON crmid = attachid
SET createdtime = FROM_UNIXTIME(`lastsavedtime`), modifiedtime = FROM_UNIXTIME(`lastsavedtime`)
WHERE some condition
SELECT *
FROM `vtiger_mailmanager_mailattachments`
inner join vtiger_crmentity on muid=crmid
UPDATE vtiger_crmentity as matt
INNER JOIN vtiger_mailmanager_mailattachments ON matt.crmid = vtiger_mailmanager_mailattachments.attachid
INNER JOIN vtiger_crmentity as mail ON mail.crmid = vtiger_mailmanager_mailattachments.muid
SET matt.createdtime = mail.createdtime, matt.modifiedtime = mail.modifiedtime
WHERE some condition
SELECT FROM_UNIXTIME(vtiger_mailmanager_mailrecord.lastsavedtime) AS unixLastSaved, vtiger_mailmanager_mailrecord.lastsavedtime, vtiger_mailmanager_mailrecord.muid, createdtime, modifiedtime, attachid, crmid
FROM vtiger_mailmanager_mailattachments
inner join vtiger_mailmanager_mailrecord on vtiger_mailmanager_mailattachments.muid = vtiger_mailmanager_mailrecord.muid
inner join vtiger_crmentity on crmid=attachid;