--------------------------- Subpanel Tabs --------------------------- * set $show_tabs=true in the SubPanelTiles.php file * added a function (get_available_tab_labels()) to SubPanelDefinitions.php file, which is called from the SubPanelTiles.php. This function is returning an array of LABELS from the layout_definition file. (title_key field of the tab definition array) The key of the returned array is the name of the tab. * store the selected tab value in the cookies ($currentModule.'_selected_subpanel'). If nothing in the cookie yet, select the first tab * "active" is the style of the selected "li" ( tabs are defined as "ul", but style is "inline", so appears on the same row) "current" is the style of the selected "a" (hyperlink) These are defined in the theme style sheet (there are some changes in the style sheet as well) * include/tabs.php file has also changed. Now using cookies to get/set selected-tab info * tabs.php file is printing out a javascript file called (selectTabCSS(key)). The key is the name of the tab. this function is setting the styles and also cookies on the client side ************************************* ************************************* --------------------- URLENCODE problem ---------------------- getbaseurl function in ListView.php (common) was not encoding the url. Fixed. ---------------------- Export into Excel ---------------------- * The following code segment is added to the "process_list_query" function in the sugarbean.php file. The session variable is later used in the export function of the module (i.e. for Contacts module, the export funciton [create_export_query] is defined in Contacts.php file) global $currentModule; $modulename= strtolower($currentModule); $_SESSION['last_'.$modulename.'_query']= $query; * Export.php function changed a lot. * Export.php function creates HTML file, but file type is defined as Excel and the extension is "xls" so that it will open directly in Excel. ---------------------- Merge Lists ---------------------- * MergeLists button is defined in the detailview of ProspectLists * it calls a javascript code "window.open(...)", the module of the url is set to ProspectLists and action is set to PopupSavedLists * PopupSavedLists.php file is similar to the code for report-import function of the professional version. It has been used as the basis, but redesigned. * when a list is selected in the popup window, it calls a javascript function "set_return" in the same file. This javascript function sets some form variables in the parent window (i.e detailview of the prospect lists) And then submits the form. It is the "detailview" form. A new field (ID) has been added to the form, which is the ID of the selected list in the popup window. The ID of the list in the detailview page of the prospect-lists (i.e. the other list from which the popup-window was opened in the detailview page) is stored in the "record" field of the form. * It is merging contacts/leads/prospects * In the contacts/leads/prospects listview screen, it is possible to store the seach result as a permanent list. Each result goes to the related part of the list. If an existing list-name is used then it will append the search result. --------------------------- Generic Message Handler --------------------------- * initialize $content_upto_currentmodulefileinclude="" variable in the main index.php file * (index.php) just before calling the current module file, assign the current output-buffer data into the above variable. *** code segment **** $content_upto_currentmodulefileinclude=ob_get_clean(); ob_start(); include($currentModuleFile); * in all the modules, make sure that all the output is gathered in a output-buffer finally, and this output-buffer is sent to the browser at the end of the index.php as follows: Note that the message (can be info or error msg) enters between two variables. This is important to show the message just below the header part of the screen, and above all the module related output *** code segment **** $technoutmsg=PrintOutClientMsg(); $curcontent = ob_get_clean(); echo $content_upto_currentmodulefileinclude.$technoutmsg.$curcontent; * AddClientMsg() function is used to add a message to the current messages-array, the first param is the message, and the second param is either "error" or "info". "error" is default if nothing is passed in * the following 3 functions in the technoutils.php are used AddClientMsg(...) ClearClientMsg() PrintOutClientMsg() --------------------------- Relationships --------------------------- Asagidaki functionlar onemli: 1) [SugarBean.php] create_relationship_meta "relationships" table icindeki relation'lari initialize ediyor. Setup aninda cagiriliyor, ayrica gerektiriginde Administration module icindeki "RebuildRelationship.php" icindende cagrilabiliyor 2) [SugarBean.php] remove_relationship_meta Nerede kullanildigini tam anlayamadim... 3) Relationships module icinde Relationship.php file'i meta data ile ilgil birkac function iceriyor function load_relationship_meta() { if (!file_exists(Relationship::cache_file_dir().'/'.Relationship::cache_file_name_only())) { $this->build_relationship_cache(); } include(Relationship::cache_file_dir().'/'.Relationship::cache_file_name_only()); $GLOBALS['relationships']=&$relationships; } function build_relationship_cache() { $query="SELECT * from relationships where deleted=0"; $result=$this->db->query($query); while (($row=$this->db->fetchByAssoc($result))!=null) { $relationships[$row['relationship_name']] = $row; } $rel_string=''; mkdir_recursive($this->cache_file_dir()); $handle=fopen(Relationship::cache_file_dir().'/'.Relationship::cache_file_name_only(),'w'); fwrite($handle,$rel_string); fclose($handle); } 4) function load_relationship($rel_name) bu function'i cagiran bean icin field_defs (butun field definitionlarinin oldugu array)'i tarayip her bir field icin once "type='link'" sartini ariyor, bu sart saglaniyorsa bean icine "field" in isminde bir variable ekliyor ve bu variable'i "new Link(... ) " seklinde init ediyor Link constructor icinde Link icindeki bir var olan "reationship"i set ederken relationship ismi, yani fieldname $GLOBALS['relationships'] icinde varmi yokmu check ediyor. Halbuki $GLOBALS['relationships'] cache'deki file'dan init ediliyor. (Ek not: file ismi --> "cache/modules/Relationships/relationships/cache.php" Bu file'a yazarken normal php arrayindan var_export(arrayname, true) function'i ile export edip yaziyor. Boyle olunca tekrar programda kullanilabilecek sekilde export edilmis oluyor.) Cache'deki file'da eger yoksa ilk kez olarak database'in relationship table'inden init ediliyor. Bu table ise Setup aninda create_relationship_meta function'i cagirilarak init ediliyor. Eger yeni relation'lar eklenmisse (module ler icindeki vardef.php dosyasina) bu process tekrarlanmali "index.php?module=Administration&action=RebuildRelationship" 5) modules/TableDictionay.php contains file includes like include_once("metadata/groups_contactsMetaData.php"); In this example, groups_contacts is defined as $dictionary['groups_contacts'] in the included file. The table definition here is not one-to-one match with module names. These are (as far as I can understand) the tables which are used to define many-to-many relationships between two modules(tables) metadata folder altinda bulunan dosyalar icinde "indices" kismi onemli. duplicate olan relationlari bulurken once right ve left taraflarin key fieldlar kullanilarak row'un unique ID'si bulunuyor, sonra update olacaksa bu ID kullaniliyor 'indices' => array ( array('name' =>'PRIMARY', 'type' =>'primary', 'fields'=>array('id')) ,array('name' =>'contact_id_event_id', 'type' =>'alternate_key', 'fields'=>array('event_id', 'contact_id')) ) ------------------------------------ Global olarak kullanilan onemli array'lar ------------------------------------- ** her module icindeki field_arrays.php icindeki $fields_array icindeki "required_fields" arrayi kullaniliyor ** layout_defs.php icindeki "$layout_defs" arrayi subpanel'leri tanimliyor. Mesela kampanya icin iki subpanel var. Bunlarin isimlerine gore [ex.. 'subpanel_name' => 'default',] "subpanels" folderi icindeki hangi file'a bakacagini anliyor. O file icinde de subpanel'de gosterilecek column'lar tanimli ** 'column_fields' arrayi (field_arrays.php icinde tanimli) database'e insert ederken kullaniliyor ------------------------------------ FLOAT formatting & other formatting ------------------------------------- 1) (at the end of EditView.php files) $javascript->addAllFields('') is called and the process starts this function is defined in the (include/javascript/javascript.php) file. It eventually calls addFieldGeneric function (explained below) 2) (include/javascript/javascript.php) function addFieldGeneric(....) in this function we have added a line which adds each field (of type float) to the format array .... $this->script .= "addToFormat('".$this->formname."', '".$prefix.$field."', '".$type. "' );\n"; .... 3) (sugar_3.js) function addToFormat(formname, name, type){ if( typeof format_form_array[formname] == 'undefined'){ addFormForFormatting(formname); } format_form_array[formname][format_form_array[formname].length] = new Array(name, type); } function addFormForFormatting(formname){ format_form_array[formname] = new Array(); } At the end of above lines, "format_form_array" array will have all the fields of type float. Each element of the array is another array with 2 fields (name,type) 4) We want to format all the text-fields (float type) once the form is loaded. We need to attach "onload" event to the "window" of the object (javascript.php) in the "addAllFields" function, $this->AttachFormatter(); This function has only a few lines (in the javascript.php) // begin yahya 1023 function AttachFormatter() { $this->script .= "eventAttacher(window, 'onload', FormatFormFields);\n"; } // end yahya "FormatFormFields" function (defined in techno.js) is attached to the window object for "onload" events This function should go through all the elements of the "format_form_array" and first format the floating numbers in the text fields and also attach "onchange" event. Onchange event should also format the number in the text field 5) Add "DoNumberFormatting()" function to the SugarBean.php file. This function to be called from DetailView.php files (if formatting is needed) 6) Some of the numbers are formatted hard-coded in the detailview.php files, ex. $xtpl->assign("BUDGET", sprintf("%4.2f", $focus->budget)); Change them as follows: $xtpl->assign("BUDGET", $focus->budget); // for JPY , we may need to modify for $ again --------------------------- Edit/Delete Permissions --------------------------- * related functions in the technoutils.php (GetActionList, GetModuleListForPermissions, CreateModuleActionMatrixHTML, GetPermission, SaveUserPermissions) * in the users module -- editview.html, editview.php, detailview.php, detailview.html, save.php have been modified * global app_strings array contains action_list array -------------------------------- Tooltips ------------------------------ * added "tooltip" folder into the include/javascript folder * added "PrintTooltipArrayJS()" and "CreateTooltipArrayForXTemp()" functions into technoutils.php * added $mod_tooltips array to the main index.php file (code segment is given below) // in the language file of each module, there should be an array-item defining tooltip labels and explanations. // e.g. in Contacts module $mod_tooltips['Contacts']=array('HLP1'=>'Description') ... // the content of this file is printed out as javascript in the PrintTooltipArrayJS function at the end of this file $mod_tooltips=array(); * $mod_tooltips array should be set for each module using tooltips in the language files * PrintTooltipArrayJS(); function should be called at the end of the main index.php file (this funciton is defined in the technoutils.php file) This function creates a javascript array (i.e. var TOOLTIP_ARRAY=new Array();) This array contains all the tooltip text of the current module * Add the following line to the php file of the current html template $xtpl->assign("TOOLTIPS", CreateTooltipArrayForXTemp()); "CreateTooltipArrayForXTemp()" function creates an array of ? marks including tag and onclick events etc * Add TOOLTIPS.xxx to the related HTML template (e.g. {TOOLTIPS.HLP_POSTCODE_SEARCH}) ------------------------------------- Popups ------------------------------------- THis will be explained using the [Account] Select button on the contact edit view * When the "select" button is clicked, "open_popup" function is called as follows: * open_popup is defined in the "include/javascript/popup_parent_helper.js" file as follows; function open_popup(module_name, width, height, initial_filter, close_popup, hide_clear_button, popup_request_data, popup_mode, create, metadata) * "popup_request_data" parameter is {encoded_account_popup_request_data}, which is defined in the EditView.php of the Account module: $popup_request_data = array( 'call_back_function' => 'set_return', 'form_name' => 'EditView', 'field_to_name_array' => array( 'id' => 'account_id', 'name' => 'account_name', ), ); $this->xtpl->assign('encoded_account_popup_request_data', $this->json->encode($popup_request_data)); * the first thing open_popup function does is : window.document.popup_request_data = popup_request_data; This line stores the "popup_request_data" value in a global variable which is defined at the top of the "include/javascript/popup_parent_helper.js" file Also, this file has another important function "get_popup_request_data()" which returns the stored global variable. * "open_popup" function creates a URL and calls it. Module of the url is the first parameter of the function. Action is "popup". Another important parameter of the url is "create". If this is set to true then popup window will have a creat Account button and related form. (this example is for accout, otherwise it would be any module) * Although the first "select" button has started the whole process on the EditView of the contact module, Popup will call the "popup.php" file of the Accounts module. "popup.php" is a simple short file with the following lines: require_once('include/Popups/Popup_picker.php'); $popup = new Popup_Picker(); echo $popup->process_page(); * Popup_picker.php is the most critical file. It does most of the processing. It create an Xtemplate with the Popup_picker.html of the Account module When the popup_picker.html is parsed, it creates a javascript which is executed immeditalety (i.e. not called from a function etc) if(window.document.forms['popup_query_form'].request_data.value == "") { window.document.forms['popup_query_form'].request_data.value = JSON.stringify(window.opener.get_popup_request_data()); } The form "popup_query_form" is defined in the same html file. One of the hidden input vars in this form is "request_data" is assigned in the Popup_picker.php file. $request_data = empty($_REQUEST['request_data']) ? '' : $_REQUEST['request_data']; $form->assign('request_data', $request_data); In the first call, "request_data" is not set in the $REQUEST array. The script segment given above will set this form variable. It calls the "window.opener.get_popup_request_data()" function of the "include/javascript/popup_parent_helper.js" file, which returns the globally stored "request_data" * At the end of all popup*.html files, there is a javascript line : {ASSOCIATED_JAVASCRIPT_DATA} This line is initialized in the global ListView.php file. First the following array is created: $associated_row_data[$fields['ID']] = $fields; This array includes all the key/values of the listview fields. Later this array is converted to javascript array using JSON as follows: if($this->process_for_popups) { require_once('include/JSON.php'); $json = new JSON(); $associated_javascript_data = ''; $this->xTemplate->assign('ASSOCIATED_JAVASCRIPT_DATA', $associated_javascript_data); } * If one of the accounts is clicked on the popup window, then it returns this value to the parent (opener) window. In the popup_picker.html file, if an account name is clicked ==> onclick="send_back('Accounts','{ACCOUNT.ID}');" The send_back function is called. This function is defined in the popup_helper.js file with "module" and "id" parameters. The first line of the function is var associated_row_data = associated_javascript_data[id]; which gets all the data of the clicked row into an array (it is explained about how the "associated_javascript_data" variable is set) This function then calls the following line: eval("var request_data = " + window.document.forms['popup_query_form'].request_data.value); This value originated at the "editview.php" file of the contacts, see above for details, but it is in json encoded form here. $popup_request_data = array( 'call_back_function' => 'set_return', 'form_name' => 'EditView', 'field_to_name_array' => array( 'id' => 'account_id', 'name' => 'account_name', ), ); "name_to_value_array" is created here using the "field_to_name_array" and "associated_javascript_data" arrays. This function eventually call the following lines and executes the "callback_function" passed in as a parameter in the request_data. var result_data = {"form_name":form_name,"name_to_value_array":name_to_value_array,"passthru_data":passthru_data}; call_back_function(result_data); Call back function is "set_return" in this example. * "set_return()" is defined in the popup_parent_helper.js file /** * The reply data must be a JSON array structured with the following information: * 1) form name to populate * 2) associative array of input names to values for populating the form */ function set_return(popup_reply_data) { var form_name = popup_reply_data.form_name; var name_to_value_array = popup_reply_data.name_to_value_array; for (var the_key in name_to_value_array) { if(the_key == 'toJSON') { /* just ignore */ } else { var displayValue=name_to_value_array[the_key]; . . . . . . . . window.document.forms[form_name].elements[the_key].value = displayValue; } } } ------------------------------------------- How to Save A Popup return value and show in the Subpanel ---------------------------------------------------------- The following code is an example for a SELECT button onclick * onclick='open_popup("Teacher",600,400,"",true,true, {"call_back_function":"set_return_and_save_background","form_name":"DetailView","field_to_name_array":{"id":"subpanel_id"}, "passthru_data":{"child_field":"Teacher", "return_url":"index.php%3Fmodule%3DCourse%26action%3DSubPanelViewer%26subpanel%3DTeacher%26record%3D5f558325-c0ab-0ae3-9add-43d499209320%26sugar_body_only%3D1", "link_field_name":"teacher","module_name":"Teacher","refresh_page":"0"}},"Single",false);' * In the call_back_function, (set_return_and_save_background), a querystring is prepared and called as follows: http_fetch_sync('index.php',query_string); The query_string contains data as follows (does not show everything here): query_array.push('value=DetailView'); query_array.push('module='+module); query_array.push('http_method=get'); query_array.push('return_module='+module); query_array.push('return_id='+id); query_array.push('record='+id); query_array.push('isDuplicate=false'); query_array.push('action=Save2'); var refresh_page = escape(passthru_data['refresh_page']); for (prop in passthru_data) { if (prop=='link_field_name') { query_array.push('subpanel_field_name='+escape(passthru_data[prop])); } else { if (prop=='module_name') { query_array.push('subpanel_module_name='+escape(passthru_data[prop])); } else { query_array.push(prop+'='+escape(passthru_data[prop])); } } } * In the save2.php file, "subpanel_fields_name" parameter is used as follows: $focus->load_relationship($_REQUEST['subpanel_field_name']); $focus->$_REQUEST['subpanel_field_name']->add($_REQUEST['subpanel_id']); The "load_relationship" function of SugarBean.php creates a "Link" object: ($rel_name is $_REQUEST['subpanel_field_name']) require_once('data/Link.php'); $this->$rel_name=new Link($fieldDefs[$rel_name]['relationship'], $this, $fieldDefs[$rel_name]); -------------------------------- Refresh ACL actions table --------------------------------- http://localhost/schoolmng/index.php?module=ACL&action=install_actions ----------------------------------------- Visible/Hidden tabs ---------------------------------------- * get_tabs() function in modules/MySettings/TabController.php is returning an array of 3 arrays. Display tabs, Hidden Tabs and Removed tabs * If we don't want to give permission for a module, we should actually put it into the removed modules list, not only to the list of hidden modules * in SugarCRM, removed modules can be set only by Admin * if a user is in the displayed list, then it will appear in the top tabs list. But if we still want to give access to the hidden module, we should add it to the $modInvisList array in the modules.php file * We have added a new array "invisible_modules" to the global lang file. It contains a list of the modules which should be removed from the top tabs list If we add those into the $modInvisList array in the modules.php file, we can still access the module --------------------------------- MySql indexes --------------------------------- * if different columns are used in an OR query, then indexes are not used. If the same column is used, then it is optimized * Use UNION if different columns are OR'ed. ----------------------------------- Relationship table duplicate check before saving ----------------------------------------- * In the "_add_many_to_many" function of the Link class (Link.php), the following row does duplicate checking //check whether duplicate exist or not. if ($this->relationship_exists($this->_relationship->join_table,$add_values)) { ....... ........ } * In the "relationship_exists" function, "_get_alternate_key_fields($table_name);" is called. If this function returns NULL then it does not check duplicates and continue This function normally should return an array of index key fields. // returns array of keys for duplicate checking, first check for an index of type alternate_key, if not found searches for // primary key. // function _get_alternate_key_fields($table_name) { $alternateKey=null; $indices=Link::_get_link_table_definition($table_name,'indices'); if (!empty($indices)) { foreach ($indices as $index) { foreach ($index as $key=>$value) { if ($key=='type' and $value=='alternate_key') { $alternateKey=$index['fields']; } } } } return $alternateKey; } * finally index keys are used to create the query for duplicate check, (also adds some other checks like deleted=0) ------------------------------- JSON_SERVER method-addition -------------------------------- * add the function name to the allowed_methods array in the jsclass_async.js file (/include/javascript/ folder) * add the function name to the $SUPPORTED_METHODS array in the json_server.php file * create a function in the json_server.php file function name will be "json_".functionname (i.e. add "json_" prefix) * return value will be an array as follows (ex) $response = array(); $response['id'] = $request_id; $response['result'] =$schedule_obj->GetScheduledTimesVersion2($schedule_obj->recurrence_info_arr['timeslot']['minute_interval']); Note: $request_id is the first parameter of the function ---------------------------- Scheduler Conflict Cases ---------------------------- * EditSchedule button is clicked on a event window. If there is already a schedule show this one otherwise start a new scheduling session If the existing schedule has conflicts show them, as well as people/resources related. * Click the CheckConflicts button on the schedule window. * Save the schedule and show the conflicts if any exists otherwise go to the detail window of the event ----------------------------------- Edit Schedule cases ----------------------------------- * sms_recurrences table id can be either event-id or contact-id, Ex. if the focus is course then event-id else if we are setting the available times, * then the id is contact_id >> do the following fixes first: 1) change the event_id field of the sms_event_instanc to "parent_id", because it can be associated with a person or event 2) add event_type field to the smsevent_instances table 3) add event_type field to the sms_event_instance table * Course (People + Resources) (Edit + EditSchedule) (Recurrence) (focus->Event) * Meeting (People + Resources) (Edit + EditSchedule) (Recurrence) (focus->Event) * Seminar (People + Resources) (Edit + EditSchedule) (Recurrence) (focus->Event) * Counseling (People + Resources) (Edit -> goes to EditSchedule) (NO Recurrence) (focus->Person) >> Note: For each counseling add a new instance to the sms_event_instance table, >> set the parent_id (old event_id) to the contact(applicant) id >> get the description etc from the schedule screen and save it to the sms_event_instance table >> for each person and resource add a row to the smsevent_instances table * Vacation (People) (Edit -> goes to EditSchedule) (NO Recurrence) (focus->Person) * Available T (People) (Edit -> goes to EditSchedule) (Recurrence) (focus->Person) >> Note: store these information in the smsevent_instances table, set the event_type fields, but leave the event_id and >> instance_id fields empty. * Holiday (Edit -> goes to EditSchedule) (Recurrence) (focus->Event) >> Note: Add a row to the smsevent_instances table for each holiday. Leave target_id blank, set the event_type to holiday >> add the name and description of the holiday to the smsevent_instances table * General Event (People + Resources) (Edit + EditSchedule) (Recurrence) (focus->Event) --------------------------- Reset ACL Actions --------------------------- http://localhost:8081/schoolmng/index.php?module=ACL&action=install_actions ----------------------------------- How to Add a hidden variable to a Custom Subpanel Button (ex. Create Button) ------------------------------------ * In the layout_defs.php file defining the subpanel, add the button widget class, this class attributes are definen in the "include/generic/LayoutManager.php" file ---> getClassFromWidgetDef() function 'top_buttons' => array( array('widget_class' => 'SubPanelTopCreateClassesButton'), ), --------------------------------------------------------- FURIKAE/ Rescheduling --------------------------------------------------------- *) The "Reschedule" icons/links in the EventInstance and EventInstanceDetail/SubpanelView.html files start the action (assuming that it is already cancelled) *) the OpenSubpanelCalendarWithRelatedInfo() javascript function is called with all the related params (parent_id, parent_module, event_id, event_module, from_instance_id, to_instance_id, status etc...) *) OpenSubpanelCalendarWithRelatedInfo function is defined in the calendar_helper.js file After doing some extra_parameter settings (which are passed to the showSubPanel()) it calls the showSubPanel function *) calendar subpanel is handled in the Subpanel.php file (ProcessCalendarSubPanelView() function) *) in the ProcessCalendarSubPanelView() function , "calendar_entrance" object is created and its prepare_for_subpanel() function is called *) ScheduleOnCalendar class defined in the templates_calendar.php file is used to handle furikae/reschedule related javascript function preparations on the BarView calendar view *) OnClickAddTargetToTimeSlot(....) javascript function in the calendar_helper.js handles the addition of a new student to a course timeslot *) OnClickAddTargetToTimeSlot function creates a tooltip menu with url-link on it. When the URL is clicked, it calls showSubPanel() javascript function with a special first parameter (normalle this parameter is the name of the subpanel). But in our case, it is "custom_handler". If the first param is custom_handler, then the returned value is not used to populate the subpanel innerHTML, instead the returned value is a javascript code which is called withing "eval" function to execute. *) AddTarget.php function in the EventInstance module is used to assign the student to the new timeslot *) AddTarget file first prepares some parameters and then calls $sch_obj=new AppendTargetToSchedule($people_resource_flag); $sch_obj->AddPeopleResourceToExistingEventInstances($target_id, $event_id, $args_to_schedule); The scheduling code used here is the same with code used in the EditRelation or addition of a student to a course from Popup (which also does scheduling) ========================================= GESSHA OLUSTURMA - Start ======================================== Gessha olusturma ile ilgili temel queriler sirasiyla (debug sirasinda olusan queriler, bir loop sadece) current gessha query SELECT sms_fee.*, DATE_ADD(sms_fee.sell_date, INTERVAL 540 MINUTE) AS fee_sell_date , contacts.id AS contact_id, contacts.grade, contacts.grade_entrance_date, CONCAT_WS(' ', contacts.last_name, contacts.first_name) AS contact_name, contacts.leave_date, sms_transaction.payment_type, contacts.contact_status, contacts.school_id AS contacts_school_id, sms_fee.school_id AS fee_school_id FROM sms_fee INNER JOIN contacts ON sms_fee.contact_id = contacts.id LEFT JOIN sms_transaction ON sms_fee.id = sms_transaction.fee_id INNER JOIN event_contacts ON sms_fee.event_id = event_contacts.event_id AND sms_fee.contact_id = event_contacts.contact_id AND event_contacts.deleted=0 AND event_contacts.event_contact_status=0 WHERE sms_fee.deleted=0 AND sms_transaction.deleted=0 AND ( sms_fee.type_id = '29' ) AND sms_fee.fee_subcategory IN ('tuitionA','tuitionB','tuitionC','textbookA', 'textbookB','textbookC', 'facilityA', 'facilityB', 'facilityC') AND contacts.custom_var12 != 'ticket' AND ( contacts.contact_status='0' ) AND contacts.deleted=0 AND contacts.smsgroup= 'student' AND ( contacts.leave_date > '2010-08-01' OR contacts.leave_date IS NULL OR contacts.leave_date='') AND sms_fee.next_gessha_applied != '1' AND 1=1 AND sms_transaction.due_date > '2010-06-30' AND sms_transaction.due_date < '2010-08-01' save gessha (one row) INSERT INTO sms_fee SET id='ae6de451-8277-d27c-a806-4c8d9d0bdba1', school_id='sssssss-1111', source_gessha_id='f1318985-d676-76dc-7e1a-4c15b168b3c4', next_gessha_applied='0', deleted='0', date_entered='2010-09-13 03:43:47', date_modified='2010-09-13 03:43:47', modified_user_id='1', created_by='1', name='??? (12??)', event_id='39e006c2-89f5-6028-fb06-4987e8f6af4b', contact_id='534f6f0b-28c6-6dca-8b8d-4987e8aef1ca', fee_subcategory='facilityA', type_id='29', basic_fee='315', applied_contact='0', jikyu_rate='1', unit_type='1', unit_no='0', period_no='3', period='0', sell_date='2010-09-16 01:00:00', due_to='10', discount_percentage='0', start_date='2010-12-09', end_date=null, product_count='0', ticket_count='0', ticket_valid='0' get salesgroup number Note: first locking some tables in function get_salesgroup_number ($school_id) in the technoutils_functions.php file then increasing invoice values , selecting it with a query, unlocking tables $query=" UPDATE resourceschool_cstm SET resourceschool_cstm.last_invoice_id_c = last_invoice_id_c + 1 WHERE resourceschool_cstm.id_c = '{$school_id}' "; save the salesgroup INSERT INTO sms_salesgroup SET id='92deed8e-7ed0-b132-3f28-4c8da15288f4', deleted='0', date_entered='2010-09-13 03:57:13', date_modified='2010-09-13 03:57:13', modified_user_id='1', created_by='1', event_id='39e006c2-89f5-6028-fb06-4987e8f6af4b', contact_id='534f6f0b-28c6-6dca-8b8d-4987e8aef1ca', salesno='1000021900' update salesno sms_fee UPDATE sms_fee SET salesno = '1000021900', applied_contact=1 WHERE id = 'ae6de451-8277-d27c-a806-4c8d9d0bdba1' create transaction INSERT INTO sms_transaction SET id='853034be-fa4f-f30a-09cd-4c8da4726566', deleted='0', date_entered='2010-09-13 04:12:36', date_modified='2010-09-13 04:12:36', modified_user_id='1', created_by='1', sell_date='2010-09-16 01:00:00', due_date='2010-10-09 15:00:00', description='??? (12??)', contact_id='534f6f0b-28c6-6dca-8b8d-4987e8aef1ca', in_amount='315', type_id='29', fee_id='ae6de451-8277-d27c-a806-4c8d9d0bdba1', event_id='39e006c2-89f5-6028-fb06-4987e8f6af4b', payment_type='hikiotoshi', salesno='1000021900' update the gessha of the last month(last time) to prevent re-creating the gessha UPDATE sms_fee SET next_gessha_applied = '1' WHERE id IN ( 'f1318985-d676-76dc-7e1a-4c15b168b3c4','10061023-c70d-4acc-2bff-4c15b137d909',.....,'29efdab7-4af5-b366-5b36-4c08dc3817cc' ) ========================================= GESSHA OLUSTURMA - End ======================================== ========================================= GESSHA Olustururken yarida bozuldugu durumu fix - Start ======================================== delete sms_salesgroup from sms_salesgroup INNER JOIN (SELECT salesno FROM `sms_fee` WHERE `date_entered` >= '2010-09-13 01:40:00' AND `date_entered` < '2010-09-13 01:55:00' group by salesno) sst ON sms_salesgroup.salesno=sst.salesno; delete sms_transaction from sms_transaction INNER JOIN (SELECT salesno FROM `sms_fee` WHERE `date_entered` >= '2010-09-13 01:40:00' AND `date_entered` < '2010-09-13 01:55:00' group by salesno) sst ON sms_transaction.salesno=sst.salesno; delete sms_fee from sms_fee INNER JOIN (SELECT salesno FROM `sms_fee` WHERE `date_entered` >= '2010-09-13 01:40:00' AND `date_entered` < '2010-09-13 01:55:00' group by salesno) sst ON sms_fee.salesno=sst.salesno; ================================================ GESSHA Olustururken yarida bozuldugu durumu fix - end ================================================ Gessha ve bankaya gonderilecek dosya ile ilgili testlerde data reset icin ============================================================= delete from sms_transaction where date_entered >'2012-01-06'; delete from sms_fee where date_entered >'2012-01-06'; delete from sms_salesgroup where date_entered >'2012-01-06'; update sms_fee set next_gessha_applied=0; KEIRI NOTES: ================ In the fee table, applied_contact field is used to understand if transactions for the fees are created and salesnos are assigned contact_id field of the fee is set if the fees are assigned to a contact. salesno field of the contact is assigned in two ways: 1) fees package has common salesno to relate the fees in the same package 2) a salesno is created and assigned to the salesno field of the fees when the transactions for these fees are created (kakutei) the new salesno overwrites the old salesno. For example, two packages are assigned to a student. the contact_id of the fees are assigned. Also each package already have two different salesnos. when the KAKUTEI button is clicked and transaction rows are created, all the fees are combined under the newly created salesno which overwrites two different salesnos owned by the fees previously. applied_contact field of the fee table is a flag (0 or 1) showing if transaction rows are created (kakute sareta ka dou ka) PERIOD, PERIOD_NO, DUE_TO fields of the fee table * period is not used now. it was designed for handling installment type payments but later the method has changed. * period_no and due_to are used together as a set. period_no shows the meaning of the due_to, so it is possible to handle different cases For exampl, if "period_no" is 0 then "due_to" stores a digit which shows the number of days after the "sell_date". The calculated date may be the "t_date" of the transaction row which is created during "kakutei" process. * 'unit_type' field of fee table: unit_type may be "hour", "0" (once payment), "1"(gessha) EXCEL ICIN VE EKRANDAKI LISTVIEW lerle ilgili not ======================= * query'ler SugarBean icindeki process_list_query() function'inda execute ediliyor ve rows icinde store edilip geri donduruluyor. Bir onceki sekliyle rowlar geri dondurulmeden once her bir row icin Bean'ler olusturuluyor ve row datalari bunlarin icine doldurulup geriye bu rowlar donduruluyordu. Performance icin simde row'lar bean lerin icine konmadan rows olarak geri donuyor * ListView.php icinde processListViewTwo() function'i onemli. Orada SugarBean'den geri donen "list" yani rows seklindeki data process ediliyor. Duruma gore normal listview yada export function'lari cagiriliyor. * Excel export functionlari bean'in get_list_view_export_data(); function'ini cagirip fields'lar uzerinde son bir degisiklik vs varsa onu yapiyor, sonra export ediliyor * Listview icin ise export'a benzer sekilde bean'in "get_list_view_data()" function'i cagiriliyor. Bu function'in cagirildigi yer ListView.php icinde processListRows() function'i. Bu function'da en son rows uzerinden sirayla gecerek ekran datasinin olustugu yer. ------------------------- Prospect list row count sinirlamasi ------------------------- Birkac yerden konrtol etmek gerekiyor. * Birisi ogrenci listview search result olarak save ederken. Bu durumda sugarbean icinde bir hook functin yazili, oradan result count check ediliyor, eger $sugar_config['max_meibolist_rows']['default'] sayisini asiyorsa (bu config module basinada set edilebilir), msg verip cikiyor, listeyi olusturmuyor. * Ikinci durum course based student list ekraninda olusturulurken daha farkli bir yere gidiyor. "include/ListView/ListView.php" icinde processSaveList() function'i icinde kontrol etmek gerekiyor. Burada da module level kontrol mumkun. * Ucuncu durum iki listeyi concat ederken. Bu durumda birlesme olduktan sonraki sayiya gore karar veriliyor. Yani duplicate olanlar disindaki toplama bakiliyor. "modules/ProspectLists/ConcatProspectLists.php" dosyasi icinde kontrol ediliyor. --------------------- ZEND browser path ---------------------- C:\Program Files\Internet Explorer\IEXPLORE.EXE C:\Program Files\Mozilla Firefox\firefox.exe ----------------------- ZEND Regular Exp ----------------------- Find: (.*)?\n Replace: '$1'=>'$1'\,\n Ex: Input Text: a b c Output Text 'a'=>'a', 'b'=>'b', 'c'=>'c', --------------- Find: (.*)?\t(.*)?\t(.*)?\t(.*)?\n Replace: '$4'=>array(\n\t'$1'\,\n\t'$2'\,\n\t'$3'\,\n\t'$4'\,\n)\,\n ex: Input text a b c d Output 'd'=>array( 'a', 'b', 'c', 'd', ), ex: find: (.*?)\n replace: '$1'=>array(\n\t'def'=>\$fields_AN\['$1'\]\,\n\t'group_name'=>'main'\,\n)\,\n Input text: dansei_cancelm_no Output text 'dansei_cancelm_no'=>array( 'def'=>$fields_AN['dansei_cancelm_no'], 'group_name'=>'main', ), ----------------------------------------------- query settings for custom paging, i.e. the listview shows the result of an array, and the array contains only the data required for the current page So this is better for performance if the full data set is not required for the current page Example: Transaction/ListViewCombini_childvillage.php ----------------------------------------------- // 1. Set $this->ListView->custom_paging=true // 2. Set $this->ListView->custom_paging_row_count (full set result without LIMIT) // 3. Set $this->ListView->custom_paging_data_already_sliced=true (the data set contains data for the current page only) ------------------------------------------------- // EXCEL PHP code samples // EXCEL PHP code samples // EXCEL PHP code samples //$this->workbook->getActiveSheet()->setCellValue('D1', PHPExcel_Shared_Date::PHPToExcel(time())); //$this->workbook->getActiveSheet()->setCellValue('E'.$row, '=C'.$row.'*D'.$row); //$this->workbook->getActiveSheet()->removeRow($baseRow-1,1); // Set active sheet index to the first sheet, so Excel opens this as the first sheet //$objPHPExcel->setActiveSheetIndex(0); // Define named ranges //$objPHPExcel->addNamedRange( new PHPExcel_NamedRange('PersonName', $objPHPExcel->getActiveSheet(), 'B1') ); // Rename named ranges //$objPHPExcel->getNamedRange('PersonName')->setName('PersonFN'); // Rename sheet //$objPHPExcel->getActiveSheet()->setTitle('Person'); // echo 'Cell Person!B1: ' . $objPHPExcel->getActiveSheet()->getCell('Person!B1')->getCalculatedValue() . "\n"; //$objPHPExcel->getActiveSheet()->setCellValue('B3', '=PersonFN & " " & PersonLN'); // Iterate worksheets // foreach ($objPHPExcel->getWorksheetIterator() as $worksheet) { // echo '- ' . $worksheet->getTitle() . "\r\n"; // // foreach ($worksheet->getRowIterator() as $row) { // echo ' - Row number: ' . $row->getRowIndex() . "\r\n"; // // $cellIterator = $row->getCellIterator(); // $cellIterator->setIterateOnlyExistingCells(false); // Loop all cells, even if it is not set // foreach ($cellIterator as $cell) { // if (!is_null($cell)) { // echo ' - Cell: ' . $cell->getCoordinate() . ' - ' . $cell->getCalculatedValue() . "\r\n"; // } // } // } // } // // Set default font //$objPHPExcel->getActiveSheet()->getDefaultStyle()->getFont()->setName('Arial'); //$objPHPExcel->getActiveSheet()->getDefaultStyle()->getFont()->setSize(10); // Set column widths //$objPHPExcel->getActiveSheet()->getColumnDimension('A')->setAutoSize(true); //$objPHPExcel->getActiveSheet()->getColumnDimension('B')->setWidth(14); // echo date('H:i:s') . " Write to CSV format\n"; // $objWriter = PHPExcel_IOFactory::createWriter($objPHPExcel, 'CSV') // ->setDelimiter(',') // ->setEnclosure('') // ->setLineEnding("\r\n") // ->setSheetIndex(0) // ->save(str_replace('.php', '.csv', __FILE__)); // // echo date('H:i:s') . " Read from CSV format\n"; // $objReader = PHPExcel_IOFactory::createReader('CSV') // ->setDelimiter(',') // ->setEnclosure('') // ->setLineEnding("\r\n") // ->setSheetIndex(0); // $objPHPExcelFromCSV = $objReader->load(str_replace('.php', '.csv', __FILE__)); -------------------------------------------------------- EXCELPHP row limits 150 MB for 3500 excel rows -------------------------- EXCEL XML TEMPLATE SAMPLE file -------------------------- A good custom excel xml creation sample School Name: excelmito modules/Keiri/ListViewTeacherBuai_excelmito.php It is creation different color rows using different xtpl templates for each row, resetting the template before each row creation, getting the text of the template without running xtlp->out() function etc. Also, see the php file preparing the excel xml templates. excelmito/preprocess_excel_xml_excelmito.php In this file, styles of the cells are set dynamically. Regular exp is used to prepare the keys to replace in the template. -------------------------------- MYSQL find replace --------------------- update [table_name] set [field_name] = replace([field_name],'[string_to_find]','[string_to_replace]'); ---------------------- [query=true] in TechnoSMS search pages works as follows ---------------------- It it is true, then it will use only the data submitted. Normally the GET params are stored in the session If query=true is NOT set, then it will populate GET array with the stored values from the session For POST submission it does NOT store submitted values in the session For Clear button, it may be necessary to set this value to true depending on the type of Clear button. Some clear buttons are submitting the form, some are just redirecting to a URL -------------------------------- about FIELD_DEFS, COLUMN_FIELDS etc arrays ------------------------------ *** In PopulateOneObjectFromOneRow() function (SugarBean), there is a loop "foreach($this->field_defs as $field=>$value) ..." assigning values in an array (which is the result of listview query in the XXXX (ModuleName).php file) to the current module object. If a value is not defined in the field_defs arrray, then it will now show up in the listview. *** column_fields array defined in the field_arrays.php in each module is used when saving a module. Save.php usually has a loop getting values from POST to the object. If a value is not defined in this array, then it will not save. *** column fields array is used for DetailView and EditView. "$this" focus object variables are copied to the XTemplate vars using column_fields array. However, if any special processing like Pull Down is required, then add it to 'detailview_special_fields' array in field_arrays.php file. *** if preporocessing is required before showing in DisplayView, then do it in "fill_in_additional_detail_fields" function *** Search form icindeki variable'lar icin SpecialAssignXtplVars() function'in cagirilmasi "query_vars" arrayinin empty olmamasinia bagli. "query_vars" variable'i ListHandler constructor function'i icinde $this->query_vars = LoadCachedArray($this->focus->module_dir, $this->focus->object_name, 'listvew_query_fields') seklinde initiliaze ediliyor. Burada dolayli olarak field_arrays.php icindeki "listvew_query_fields" array'inin empty olmamasi gerekiyor. Empty olursa search form'u icindeki variable'larin auto assign edilmesi yada SpecialAssignXtplVars() function'i icinde THEME var assign edilmesi calismiyor. *** Search form icindeki variable'larin search sonrasi degerlerini korumsasi icin, ve ayni zamanda pull-down'larin duzgun assign edilmeleri icin field_arrays.php icindeki "listvew_query_fields" array'ine eklenmesi gerekiyor. Sadece burasi yeterli search form degerlerini muhafaza edebilmek icin vardefs.php icinde olmasa bile OK, yada field_arrays.php icindeki "column_fields" arrayinde olmasa bile problem yok ------------------------------------------------------------------- COURSE CATEGORY ve COURSE Popuplarinin Search ekranina yerlestirilmesi ---------------------------------------------------------------- oncelikle course name ve category name Disabled olduklarindan submit edilmiyor parent_id ve event_id'ler hidden olarak submit ediliyor bu bilgiler kullanilarak Name bilgilerinin bulunmasi gerekiyor Bu amacla en genel cozum class seviyesinde var $category_obj; tanimlamak ve bunu en basta init etmek Oncelikle require_once("modules/CourseCategory/CourseCategory.php"); gerekli. Sonrasinda class constructor icinde $this->category_obj = new CourseCategory(); Query kisminda course ve course-category eklenmesi asagidaki ornekteki gibi $query="select c.id as contact_id, c.last_name, c.first_name, CONCAT_WS(' ', c.last_name, c.first_name) AS contact_name, c.first_name_furigana, c.last_name_furigana, c.idnumber, e.id as course_id, e.name as course_name, ei.from_datetime, eid.attendance_status FROM contacts c INNER JOIN event_contacts ec ON ec.contact_id=c.id AND ec.deleted=0 INNER JOIN smsevent e ON ec.event_id = e.id AND e.deleted=0 INNER JOIN sms_event_instance ei ON ei.parent_id= e.id AND ei.deleted=0 INNER JOIN smsevent_instances eid ON ei.id= eid.instance_id AND e.id=eid.event_id AND eid.target_id=c.id "; if (isset($_REQUEST['parent_id']) && !empty($_REQUEST['parent_id'])) { $query .= " LEFT JOIN course_category ON e.parent_id = course_category.id AND course_category.deleted=0 LEFT JOIN category_tree ON course_category.id = category_tree.self_id "; } $query .= " WHERE (ei.from_datetime < '{$next_month}') AND (ei.from_datetime >= '{$input_month}') AND (c.smsgroup='student' AND c.contact_status=0) "; if (isset($_REQUEST['parent_id']) && !empty($_REQUEST['parent_id'])) { $this->category_obj->retrieve($_REQUEST['parent_id']); $subcategories = $this->category_obj ->GetCategoryIdsBelow($this->category_obj ->get_node_id($this->category_obj ->id)); $query .=" AND category_tree.node_id IN ( '" . implode("', '", $subcategories) . "' ) "; } if (isset($_REQUEST['event_id']) && !empty($_REQUEST['event_id'])) { $query .=" AND e.id='{$_REQUEST['event_id']}' "; } Search formundaki var initialization icin asagidaki ornek , normalde SpecialAssignXtplVars() function'i icinde olabilir if (isset($_REQUEST['parent_id']) && !empty($_REQUEST['parent_id'])) { $this->category_obj->retrieve($_REQUEST['parent_id']); $this->search_form->assign("PARENT_CATEGORY_NAME", $this->category_obj->name); $this->search_form->assign("PARENT_ID", $_REQUEST['parent_id']); //$this->search_form->assign("PARENT_NODE_ID", $_REQUEST['parent_node_id']); } if (isset($_REQUEST['event_id']) && !empty($_REQUEST['event_id'])) { $this->search_form->assign("EVENT_ID", $_REQUEST['event_id']); $this->search_form->assign("COURSE_NAME", get_event_name_by_event_id($_REQUEST['event_id'])); } HTML kisminda ise (search form) {MOD.LBL_COURSE_CATEGORY} {MOD.LBL_COURSE} -------------------------------------------------------------------- Edit ekranini yeniden Msg ile birlikte gostermek icin (error message ile birlikte edit ekrani tekrar gosteriliyor) ------------------------------------------------------------------- Ornek program Entrance module'unden Save.php function'i icinde $redisplay_current_page=false; if (!isset($_REQUEST['contact_id']) || empty($_REQUEST['contact_id'])) { AddClientMsg(_t("ERR_EMPTY_STUDENT_NAME")); $redisplay_current_page=true; } if ($redisplay_current_page) { $_REQUEST['carry_msg']=1; $_REQUEST['action']='EditView'; $_REQUEST = array_map("securexss_reverse", $_REQUEST); $_POST = array_map("securexss_reverse", $_POST); $_GET = array_map("securexss_reverse", $_GET); $GLOBALS['show_request_vars']=true; // in editviewparent, the fields are re-initialized with the values from REQUEST, not from focus obj fields // if we want to re-init a field as a special case, for example not from REQUEST, but always a fix value etc, then use this global arr value to handle special cases unset($modListHeader); // set in the index.php again, a list of modules allowed include("index.php"); exit; } -------------------------------------------------------------------------- sql temporary table join sample -------------------------------------------------------------------------- $query="SELECT c.id as contact_id, c.last_name, c.first_name, CONCAT_WS(' ', c.last_name, c.first_name) AS contact_name, SUBSTR(DATE_ADD( ec_dates.max_end_date, INTERVAL {$time_zone_adjustment} MINUTE), 1, 10) as setsumeikai, t_contacts.contact_name as shokaikoshi, c.description FROM contacts c LEFT JOIN ( select ec.contact_id, max(ec.end_date) max_end_date from event_contacts ec INNER JOIN smsevent e ON e.id=ec.event_id AND ec.deleted=0 AND e.deleted=0 and e.subtype=4 group by ec.contact_id ) ec_dates ON ec_dates.contact_id = c.id LEFT JOIN ( select c1.id as contact_id, u.id as user_id, CONCAT_WS(' ', c1.last_name, c1.first_name) AS contact_name from contacts c1 inner join users u on u.contact_id=c1.id where c1.deleted=0 AND u.deleted=0 ) t_contacts ON t_contacts.user_id = c.reports_to_id "; ----------------------------------------------------------------------------- checklist display ---------------------------------------------------------------------------- burada ikinci parametre (custom_enum6), $app_list_strings icindeki array key $row['baitai']=checklist_show_value($row['baitai'], "custom_enum6"); ---------------------------------------------------------------------------- ListView custom list (list query, paging, excel check etc, hepsinin custom yapildigi ornek) ----------------------------------------------------------------------------- $this->list_to_process[]=$row; // row'lar bu araya doldurulup custom process ediliyor Asagidaki file bircok ozelligi iceriyor, * ORDERBY vs dahil, yani listview'deki klasik order ozelliginin kullanilmasi * count query'si olustururken birden cok FROM yada bir field icinde (from_date) from kelimesi geciyorsa default function problem olusturuyor onun yerine burada ayri bir count query olusturan function var * excel ise all rows, degilse sadece bir page icin olan kismin gosterilmesi, Excel allowed row count' tan daha fazla row varsa warning verip excel gostermeme ozelligi SCHOOLCUSTOM/hegl/modules/Student/ListViewNyukaiMikomi_hegl.php -------------------------------------------------------------------------------- myql sort order by (string olan bir field icin integer olarak sort etmek gerekirse) sample code from SCHOOLCUSTOM/hegl/modules/Student/ListViewNyukaiMikomi_hegl.php ----------------------------------------------------------------------- if (isset($orderby) && !empty($orderby)) { if ($orderby =='grade desc' || $orderby == 'grade asc' || $orderby=='grade') { if ($orderby == 'grade asc' || $orderby=='grade') { $suffix="asc"; } else $suffix="desc"; $orderby=" case when c.grade='general' then 100 else c.grade + 0 end {$suffix} "; } else $orderby="c.{$orderby}"; $query .= " ORDER BY {$orderby}, c.date_entered DESC "; } else { $query .= " ORDER BY c.application_date DESC, c.date_entered DESC "; } ----------------------------------------------------------------------------- // TRANSACTION veya FEE rowloarin kopyalanmasi ------------------------------------------------------------------------ Noborders'da boyle bir case var. Belli bir ayin gessha fee ve transaction'lari topluca birkac degisiklik yapilip kopyalaniyor. Burada problem datetime'lar. Source date GM time'da oldugundan bir kez daha kopyalanip save edilince 9 saat daha geriye gidiyor, yanlis oluyor. Bunu cozmek icin once 9 saat ileri alip save ediyoruz Ornek file: SCHOOL_CUSTOM/noborders/modules/Transaction/GesshaJidouSeikyu_noborders.php Ornek function name: function HandleTransactions() ... -------------------------------------------------------------------- ----------------------------------------------------------------------- batch processing settings ----------------------------------------------------------------------- Sample files: SCHOOLCUSTOM/caplan/modules/Teacher/CheckListViewCls_caplan.php SCHOOLCUSTOM/biokura/modules/Student/include/StudentCourseExportCls_biokura.php tables: batch_manager ve batch_items Herbir process icin bir config arrayi var. Sample file Caplan icin SCHOOLCUSTOM/caplan/include/Cronjob/batch_config_caplan.php Mesela: $GLOBALS['batch_config']=array( 'teacher_sch_excel'=>array( 'name_prefix'=>'�u�t�X�P�W���[���ꊇ�쐬', 'interval_secs'=>1, // seconds //'start_time'=>'14:35:00', // if later than local current time automatically shifts to next day 'items_each_time'=>20, 'max_in_category'=>1, // max in history of the same category/type 'mail_notification'=>0, 'mail_notification_info'=>array( 'address'=>'yahya.aydin@technopian.com', ..... ), 'class_path'=>include_custom('modules/Teacher/include/CheckListViewCls_caplan.php'), 'class_name'=>'CheckListViewTeacher', 'function_name'=>'ProcessBatchReports', 'xls_template'=>get_custom_path().'modules/Teacher/include/xml_template/teacher_schedule_template.xls', ), ); Bu array'dan "teacher_sch_excel => "batch_manager" table'indaki "category" start_date => zaman'i 00:00:00 aliniyor ve batch_manager daki "date_time_start " oluyor (save ederken GM'e ceviriliyor) start_time => henuz gun icinde bu saat gelmemisse bugunden itibaren gecmisse yarindan itibaren olacak sekilde set ediliyor ve "date_time_start" field'inda save ediliyor save etmeden once GM'e ceviriliyor, config fileinda local time school_id => gerekliyse batch_manager'a ekle, set edilmezse su anda null oluyor freeparam1, freeparam2, freeparam3 set edilip sonra kullanilabilir batch_items TABLE settings -------------------------- batch_items table icinde "item_key", "item_info1", "item_info2", "item_info3", "item_info4" field'lari onemli her bir item icin unique olacak sekilde item_key items array'i icinde set edilmis olarak create batch function'i cagirilmali diger "info 1,23,4 " field'lari ihtiyaca gore eklenecek ----------------------------------------------------------------------------- batchcron.php ve batchprocess.php files ----------------------------------------------------------------------------- batchcron.php dosyasi cronjob tarafindan cagriliyor sonucta BatchManager.php dosyasi icindeki "run_active_batches()" function'i cagirliyor. Bu functin icinde batch_manager'dan row'lar getiriliyor ve "deleted" olanlar icin $this->clean_deleted_files($row); cagiriliyor Deleted olanlari clean ederken degisik case'ler var. Bazi kisimlari cikarilmis (simplified haliyle function asagidaki gibi) function clean_deleted_files(&$row) { $path=get_cache_perm_path()."batch"; $batch_id=$row['id']; // no files to delete, skip file-delete part if (isset($row['freeparam1']) && $row['freeparam1']=='no_file_to_export') { // one file created (not for each batch item), delete only this file whose name is stored in the "filename" field of the batch_manager } else if (isset($row['freeparam1']) && $row['freeparam1']=='only_one_file') { $filename=$row['filename']; if (!empty($filename) && file_exists($filename)) unlink($filename); // there is one file for each item } else { $query="select id from batch_items where batch_id='{$batch_id}' AND status=1 AND deleted=1"; $result = $this->db->query($query); while($item = $this->db->fetchByAssoc($result)){ $filename=$path."/".$item['id']; if (file_exists($filename)) unlink($filename); } } } Delete islemi iki asamali, once sadece batch_manager icinde "deleted=1" oluyor, sonra "deleted=1" olanlar icin butun item file'lari temizlenip, batch_items fiziksel olarak delete edildikten sonra batch_manager icindeki row kaliyor, ancak deleted=2 oluyor Sonra, batch config icindeki setting'lere gore daily, monthly, yearly ise, bu sefer process edilip edilmeyecegine ozel olarak bakiliyor Her Cronjob cagirilisinda her bir config (category) icin hangi class/function cagirilacagi config icinde var, $class_name=$batch_info['class_name']; $class_path=$batch_info['class_path']; $function_name=$batch_info['function_name']; Class instance olusturup function cagiliyor, bu instance su anki batch_manager row'u da geciriliyor require_once($class_path); $obj=new $class_name; $obj->batch_manager_row=$row; $process_result=$obj->$function_name(); EN sonunda ise batch'in son durumu update ediliyor if ($process_result===true) { $this->update_batch_status($row, $batch_info); } ---------------------------------------------------------------------------------------------- Send File from server to browser ---------------------------------------- sample function "SendFileFromServerToBrowser()" in technoutils_functions.php one way of doing this is to use "fpassthru" php command to send it to the server finally also there are different ways, i could not see a clear difference on my web search, for dowloading very big files, fpassthru looks better, because it seems that it uses less memory (though some people claim that no difference with others), but there might be some security risk (??), i don't know what it is ------------------------------------------------------------- Javascript checkbox toggle all ------------------------------------ asagidaki file icinde javascript function var, cagirilan yerde yine ayni file icinde SCHOOLCUSTOM/caplan/modules/Teacher/html/CheckList_weekly_schedule.html -------------------------------------------------------