├── LICENSE ├── README.md ├── apexplugin.json ├── images ├── preview.gif ├── preview_collapse.gif ├── preview_collapse_all.gif ├── preview_expand_all.gif ├── preview_helptext.gif └── preview_refresh.gif ├── plugin_install.sql ├── server ├── mustache.js ├── pretius_main.js ├── pretius_nestedreport.js └── pretius_row_details_styles.css └── src ├── PRETIUS_APEX_NESTED_REPORTS.plb └── PRETIUS_APEX_NESTED_REPORTS.sql /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 Pretius Sp. z o.o. Sp. K. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pretius Nested Reports 2 | 3 | Oracle APEX dynamic action plugin v2.0.2 4 | The plugin is dynamic action plugin implementing nested reports within APEX Classic Reports, Interactive Reports and static HTML tables. Scope of data, data appearance and behavior is customizable with the plugin attributes. 5 | 6 | ## Table of Contents 7 | - [Preview](#preview) 8 | - [License](#license) 9 | - [Features at glance](#features-at-glance) 10 | - [Roadmap](#roadmap) 11 | - [Installation](#installation) 12 | - [Installation package](#installation-package) 13 | - [Installation procedure](#installation-procedure) 14 | - [Usage guide & Demo application](#usage-guide-demo-application) 15 | - [Free support](#free-support) 16 | - [Bug reporting and change requests](#bug-reporting-and-change-requests) 17 | - [Implementation issues](#implementation-issues) 18 | - [Become a contributor](#become-a-contributor) 19 | - [Comercial support](#comercial-support) 20 | - [Changelog](#changelog) 21 | - [Known issues](#known-issues) 22 | - [About Author](#about-author) 23 | - [About Pretius](#about-pretius) 24 | 25 | ## Preview 26 | ![Alt text](images/preview.gif?raw=true "Preview") 27 | 28 | ## License 29 | MIT 30 | 31 | ## Features at Glance 32 | * Compatible with Classic report, Interactive report and any HTML based table 33 | * Nested report is defined as SQL query 34 | * Nested report data can be limited with value from parent report 35 | * Nesting levels is unlimited 36 | * Data can be rendered with default template (table based) or with custom template (Mustache library) 37 | * Default callback and Default template can be highly customized 38 | 39 | ## Roadmap 40 | * [ ] "No data found" attribute should be translatable 41 | * [ ] Default template: 42 | * filtering data via columns 43 | * sorting data via columns (db side) 44 | * number of rows 45 | * [x] Plugin events on collapsing and expanding row (default callback) 46 | * [x] Support for Interactive report 47 | * [x] More attributes to customize 48 | 49 | ## Installation 50 | 51 | ### Installation package 52 | 1. `src/PRETIUS_APEX_NESTED_REPORTS.sql` - the plugin package specification 53 | 1. `src/PRETIUS_APEX_NESTED_REPORTS.plb` - the plugin package body 54 | 1. `plugin_install.sql` - the plugin installation files for Oracle APEX 5.1 or higher 55 | 56 | ### Installation procedure 57 | To successfully install/update the plugin follow those steps: 58 | 1. Install package `PRETIUS_APEX_NESTED_REPORTS` in Oracle APEX Schema owner (ie. via SQL Workshop) 59 | 1. Install the plugin file `plugin_install.sql` using Oracle APEX plugin import wizard 60 | 1. Configure application level componenets of the plugin 61 | 62 | ## Usage guide & Demo application 63 | ### Basic usage 64 | Example nested report is based on `emp` and `dept` table. 65 | 1. Create new application 66 | 1. Create new page 67 | 1. Create `Classic Report` based on SQL query `*` 68 | 1. Create derivied column and configure it as follows: 69 | 1. Change derivied column `Type` to `Link` 70 | 1. Set `Target` to `URL` 71 | 1. Set `URL` to `javascript: void(0);` and click `OK` 72 | 1. Set `Link Text` to `` 73 | 1. Set `Link Attributes` to `class="dept"` 74 | 1. Create new dynamic action and configure it as follows: 75 | 1. Set `Event` to `Click` 76 | 1. Set `Selection Type` to `jQuery Selector` 77 | 1. Set `jQuery Selector` to `.dept` 78 | 1. Set `Event Scope` to `Dynamic` 79 | 1. Set `Static Container (jQuery Selector)` to `body` 80 | 1. Create true action and configure it as follows: 81 | 1. Set `Action` to `Pretius Nested Reports [Plug-In]` 82 | 1. Set `Details query` to `**` 83 | 1. Set `Affected Elements > Selection Type` to `Region` 84 | 1. Set `Affected Elements > Region` to `Classic Report` defined in step 3. 85 | 1. (Not required) Adjust the plugin behaviour up to your needs using the plugin attributes 86 | 1. Save and run page 87 | 88 | `* SQL Query for step 3` 89 | ```sql 90 | select * from dept 91 | ``` 92 | `** SQL Query for step 6.ii` 93 | ```sql 94 | select * from emp where deptno = '#DEPTNO#' 95 | ```` 96 | ### Demo application 97 | Check different plugin configurations and use cases in our [Live Demo](http://apex.pretius.com/apex/f?p=105:NESTED_REPORTS) 98 | 99 | ## Free support 100 | Pretius provides free support for the plugins at the GitHub platform. 101 | We monitor raised issues, prepare fixes, and answer your questions. However, please note that we deliver the plug-ins free of charge, and therefore we will not always be able to help you immediately. 102 | 103 | Interested in better support? 104 | * [Become a contributor!](#become-a-contributor) We always prioritize the issues raised by our contributors and fix them for free. 105 | * [Consider comercial support.](#comercial-support) Options and benefits are described in the chapter below. 106 | 107 | ### Bug reporting and change requests 108 | Have you found a bug or have an idea of additional features that the plugin could cover? Firstly, please check the Roadmap and Known issues sections. If your case is not on the lists, please open an issue on a GitHub page following these rules: 109 | * issue should contain login credentials to the application at apex.oracle.com where the problem is reproduced; 110 | * issue should include steps to reproduce the case in the demo application; 111 | * issue should contain description about its nature. 112 | 113 | ### Implementation issues 114 | If you encounter a problem during the plug-in implementation, please check out our demo application. We do our best to describe each possible use case precisely. If you can not find a solution or your problem is different, contact us: apex-plugins@pretius.com. 115 | 116 | ## Become a contributor! 117 | We consider our plugins as genuine open source products, and we encourage you to become a contributor. Help us improve plugins by fixing bugs and developing extra features. Comment one of the opened issues or register a new one, to let others know what you are working on. When you finish, create a new pull request. We will review your code and add the changes to the repository. 118 | 119 | By contributing to this repository, you help to build a strong APEX community. We will prioritize any issues raised by you in this and any other plugins. 120 | 121 | ## Comercial support 122 | We are happy to share our experience for free, but we also realize that sometimes response time, quick implementation, SLA, and instant release for the latest version are crucial. That’s why if you need extended support for our plug-ins, please contact us at apex-plugins@pretius.com. 123 | We offer: 124 | * enterprise-level assistance; 125 | * support in plug-ins implementation and utilization; 126 | * dedicated contact channel to our developers; 127 | * SLA at the level your organization require; 128 | * priority update to next APEX releases and features listed in the roadmap. 129 | 130 | 131 | ## Changelog 132 | 133 | ### 2.0.2 134 | - `PL/SQL` fix to multiple columns value placeholders by @darshanputtaswamy https://github.com/Pretius/apex-nested-reports/issues/16 135 | - `PL/SQL` fix to `ORA-06502: PL/SQL: numeric or value error` by @rimblas https://github.com/Pretius/apex-nested-reports/issues/7 136 | - `JS` jQuery `size` method removed in favor for `length` property - compatibility for APEX 18+ 137 | 138 | 139 | ### 2.0.0 / 2.0.1 140 | - `JS` Highlighting nested reports no longer embeds CSS rules within DOM as tag 141 | - `JS` Default callback supports special events that can be triggered using anchor with special classes (refresh, expand all, collapse, collapse all) 142 | - `JS` The plugin triggers event on collapsing and expanding nested report 143 | - `JS` The plugin can be bound with "dialog close" event to refresh nested report after closing modal page 144 | - `JS` The plugin no longer supports TR tag as triggering element 145 | - `JS` HTML within nested report is fully supported 146 | - `JS` Nested report can be bound to any HTML table (the plugin is not limited to Classic or Interactive Report) 147 | - `PL/SQL` "#COLUMN_VALUE#" marker should be surrounded with apostrophes to avoid APEX query compilation error. When value is number, apostrophes are removed by the plugin automatically. 148 | - `PL/SQL` Nested report uses DBMS_SQL.BIND_VARIABLES to bind values from GUI (APEX items included) 149 | - `PL/SQL` The plugin supports special column aliases in nested report to create headings without content or font-awesome icon as heading 150 | - `PL/SQL` The plugin supports SQL comments within nested report query 151 | - `Plugin` Interactive Report is supported 152 | - `Plugin` The plugin no longer checks compatibility between nested report and parent report. Setting the plugin is easier and only requires existing span tag with proper class and value marker 153 | - `Plugin` The plugin supports embedding nested report within nested reports (the number of nested reports is not limited by the plugin) 154 | - `Plugin` The plugin configuration errors are now displayed as nested report with error and hint (not only in console) 155 | - `Plugin` The plugin javascript and PL/SQL code was written from very beginning. 156 | - `Plugin attributes` New styles of "Default callback" loading indicator (align to report, align to cell, embed in cell, replace cell content). 157 | - `Plugin attributes` Data rendered in "Default template" can be sorted by end-user. Javascript sorting function uses column type (varchar2 or number) 158 | - `Plugin attributes` New plugin attribute for extending "Default template" HTML with preceding and following HTML. 159 | - `Plugin attributes` Default template supports stretching 160 | - `Plugin attributes` Animation duration for expanding and collapsing nested report can be specified in „Plugin component settings” 161 | 162 | ### 1.0.0 163 | - Initial release 164 | 165 | ## Known issues 166 | * Column alias can't contain period - https://github.com/Pretius/apex-nested-reports/issues/13 167 | 168 | ## About Author 169 | Author | Website | Github | Twitter | E-mail 170 | ------------------|-----------------------------------------|----------------------------------------------|-----------------------------------------------|---------------------------------------------------- 171 | Bartosz Ostrowski | [http://ostrowskibartosz.pl](https://www.ostrowskibartosz.pl) | [@bostrowski](https://github.com/bostrowski) | [@bostrowsk1](https://twitter.com/bostrowsk1) | bostrowski@pretius.com, ostrowski.bartosz@gmail.com 172 | 173 | ## About Pretius 174 | Pretius Sp. z o.o. Sp. K. 175 | 176 | Pretius is a software company specialized in Java-based and low-code applications, with a dedicated team of over 25 Oracle APEX developers. 177 | Members of our APEX team are technical experts, have excellent communication skills, and work directly with end-users / business owners of the software. Some of them are also well-known APEX community members, winners of APEX competitions, and speakers at international conferences. 178 | We are the authors of the translate-apex.com project and some of the best APEX plug-ins available at the apex.world. 179 | We are located in Poland, but working globally. If you need the APEX support, contact us right now. 180 | 181 | Address | Website | E-mail 182 | --------|---------|------- 183 | Żwirki i Wigury 16A, 02-092 Warsaw, Poland | [http://www.pretius.com](http://www.pretius.com) | [office@pretius.com](mailto:office@pretius.com) 184 | 185 | 186 | 187 | -------------------------------------------------------------------------------- /apexplugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "Pretius Nested Reports", 3 | "version" : "2.0.2", 4 | "description" : "The plugin is dynamic action plugin implementing nested reports within APEX Classic Reports, Interactive Reports and static HTML tables. Scope of data, data appearance and behavior is customizable with the plugin attributes.", 5 | "keywords" : ["nested", "details", "table", "row", "master detail"], 6 | "homepage" : "https://github.com/Pretius/apex-nested-reports", 7 | "bugs" : { 8 | "url" : "https://github.com/Pretius/apex-nested-reports/issues", 9 | "email" : "apex-plugins@pretius.com" 10 | }, 11 | "license" : "MIT", 12 | "commercial" : "No", 13 | "commercially_supported":"Yes", 14 | "author" : { 15 | "name" : "Pretius", 16 | "email" : "apex-plugins@pretius.com", 17 | "url" : "http://www.pretius.com", 18 | "twitter": "PretiusSoftware" 19 | }, 20 | "repository" : { 21 | "type" : "git", 22 | "url" : "https://github.com/Pretius/apex-nested-reports.git" 23 | }, 24 | "oracle": { 25 | "versions": [ 26 | "11.2.0.1", 27 | "12.1.0.1", 28 | "12.2.0.1", 29 | "18.0.0.0" 30 | ], 31 | "apex": { 32 | "versions": [ 33 | "5.1.0", 34 | "18.1.0", 35 | "18.2.0", 36 | "19.1.0", 37 | "19.2.0", 38 | "20.1.0" 39 | ], 40 | "plugin" : { 41 | "internalName" : "apex.nested.reports", 42 | "type" : "dynamic action", 43 | "demo" : "http://apex.pretius.com/apex/f?p=105:NESTED_REPORTS", 44 | "previewImage" : "https://raw.githubusercontent.com/Pretius/apex-nested-reports/master/images/preview.gif" 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /images/preview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pretius/apex-nested-reports/ee346cbdb1dfcb4709aaef08552566117fd1d694/images/preview.gif -------------------------------------------------------------------------------- /images/preview_collapse.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pretius/apex-nested-reports/ee346cbdb1dfcb4709aaef08552566117fd1d694/images/preview_collapse.gif -------------------------------------------------------------------------------- /images/preview_collapse_all.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pretius/apex-nested-reports/ee346cbdb1dfcb4709aaef08552566117fd1d694/images/preview_collapse_all.gif -------------------------------------------------------------------------------- /images/preview_expand_all.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pretius/apex-nested-reports/ee346cbdb1dfcb4709aaef08552566117fd1d694/images/preview_expand_all.gif -------------------------------------------------------------------------------- /images/preview_helptext.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pretius/apex-nested-reports/ee346cbdb1dfcb4709aaef08552566117fd1d694/images/preview_helptext.gif -------------------------------------------------------------------------------- /images/preview_refresh.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pretius/apex-nested-reports/ee346cbdb1dfcb4709aaef08552566117fd1d694/images/preview_refresh.gif -------------------------------------------------------------------------------- /server/mustache.js: -------------------------------------------------------------------------------- 1 | (function defineMustache(global,factory){if(typeof exports==="object"&&exports&&typeof exports.nodeName!=="string"){factory(exports)}else if(typeof define==="function"&&define.amd){define(["exports"],factory)}else{global.Mustache={};factory(global.Mustache)}})(this,function mustacheFactory(mustache){var objectToString=Object.prototype.toString;var isArray=Array.isArray||function isArrayPolyfill(object){return objectToString.call(object)==="[object Array]"};function isFunction(object){return typeof object==="function"}function typeStr(obj){return isArray(obj)?"array":typeof obj}function escapeRegExp(string){return string.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g,"\\$&")}function hasProperty(obj,propName){return obj!=null&&typeof obj==="object"&&propName in obj}var regExpTest=RegExp.prototype.test;function testRegExp(re,string){return regExpTest.call(re,string)}var nonSpaceRe=/\S/;function isWhitespace(string){return!testRegExp(nonSpaceRe,string)}var entityMap={"&":"&","<":"<",">":">",'"':""","'":"'","/":"/","`":"`","=":"="};function escapeHtml(string){return String(string).replace(/[&<>"'`=\/]/g,function fromEntityMap(s){return entityMap[s]})}var whiteRe=/\s*/;var spaceRe=/\s+/;var equalsRe=/\s*=/;var curlyRe=/\s*\}/;var tagRe=/#|\^|\/|>|\{|&|=|!/;function parseTemplate(template,tags){if(!template)return[];var sections=[];var tokens=[];var spaces=[];var hasTag=false;var nonSpace=false;function stripSpace(){if(hasTag&&!nonSpace){while(spaces.length)delete tokens[spaces.pop()]}else{spaces=[]}hasTag=false;nonSpace=false}var openingTagRe,closingTagRe,closingCurlyRe;function compileTags(tagsToCompile){if(typeof tagsToCompile==="string")tagsToCompile=tagsToCompile.split(spaceRe,2);if(!isArray(tagsToCompile)||tagsToCompile.length!==2)throw new Error("Invalid tags: "+tagsToCompile);openingTagRe=new RegExp(escapeRegExp(tagsToCompile[0])+"\\s*");closingTagRe=new RegExp("\\s*"+escapeRegExp(tagsToCompile[1]));closingCurlyRe=new RegExp("\\s*"+escapeRegExp("}"+tagsToCompile[1]))}compileTags(tags||mustache.tags);var scanner=new Scanner(template);var start,type,value,chr,token,openSection;while(!scanner.eos()){start=scanner.pos;value=scanner.scanUntil(openingTagRe);if(value){for(var i=0,valueLength=value.length;i0?sections[sections.length-1][4]:nestedTokens;break;default:collector.push(token)}}return nestedTokens}function Scanner(string){this.string=string;this.tail=string;this.pos=0}Scanner.prototype.eos=function eos(){return this.tail===""};Scanner.prototype.scan=function scan(re){var match=this.tail.match(re);if(!match||match.index!==0)return"";var string=match[0];this.tail=this.tail.substring(string.length);this.pos+=string.length;return string};Scanner.prototype.scanUntil=function scanUntil(re){var index=this.tail.search(re),match;switch(index){case-1:match=this.tail;this.tail="";break;case 0:match="";break;default:match=this.tail.substring(0,index);this.tail=this.tail.substring(index)}this.pos+=match.length;return match};function Context(view,parentContext){this.view=view;this.cache={".":this.view};this.parent=parentContext}Context.prototype.push=function push(view){return new Context(view,this)};Context.prototype.lookup=function lookup(name){var cache=this.cache;var value;if(cache.hasOwnProperty(name)){value=cache[name]}else{var context=this,names,index,lookupHit=false;while(context){if(name.indexOf(".")>0){value=context.view;names=name.split(".");index=0;while(value!=null&&index")value=this.renderPartial(token,context,partials,originalTemplate);else if(symbol==="&")value=this.unescapedValue(token,context);else if(symbol==="name")value=this.escapedValue(token,context);else if(symbol==="text")value=this.rawValue(token);if(value!==undefined)buffer+=value}return buffer};Writer.prototype.renderSection=function renderSection(token,context,partials,originalTemplate){var self=this;var buffer="";var value=context.lookup(token[1]);function subRender(template){return self.render(template,context,partials)}if(!value)return;if(isArray(value)){for(var j=0,valueLength=value.length;j'+this.options.action.attribute13.split( this.defaultTemplateMarker )[0]+'', 72 | defaultTemplateAfter : this.options.action.attribute13 == null ? null : '
'+this.options.action.attribute13.split( this.defaultTemplateMarker )[1]+'
' 73 | }; 74 | 75 | this.td = this.element.is('td') ? this.element : this.element.closest('td'); 76 | 77 | this.affectedElement = this.options.affectedElements; 78 | //closest table to triggering element 79 | this.table = this.td.closest('table'); 80 | //closest row to triggering element 81 | this.row = this.td.closest('tr'); 82 | //object describing nested report entity 83 | this.nestedReport = { 84 | ajaxData: null, 85 | tr: null, 86 | td: null, 87 | container: null, 88 | contentHere: null 89 | }; 90 | 91 | this.spinner = null; 92 | 93 | this.inception = { 94 | 'type' : undefined, 95 | 'element': undefined, 96 | 'level' : undefined 97 | }; 98 | 99 | this.expanded = false; 100 | this.animationRunning = false; 101 | this.queryColumnsNames = this.options.queryColumns; 102 | this.queryItems = this.options.queryItems; 103 | this.queryColumnsValues = []; 104 | 105 | this.ajax = { 106 | id: this.options.action.ajaxIdentifier, 107 | running: false, 108 | forced: false, 109 | isRefresh: false 110 | }; 111 | 112 | this.td 113 | .on('mouseenter', $.proxy( this._highlight,this )) 114 | .on('mouseleave', $.proxy( this._removeHighlight,this )); 115 | 116 | this.row 117 | .on('mouseenter', $.proxy( this._overrideApexTrHover,this, true )) 118 | .on('mouseleave', $.proxy( this._overrideApexTrHover,this, false )); 119 | 120 | //? 121 | if ( this.options.throwError != undefined ) { 122 | this.throwError( this.options.throwError, this.errorTypes.configuration ); 123 | } 124 | 125 | notMatchedColumn = this._notMatchedColumn(); 126 | 127 | this.inception = this._iAmYourFatherLuke(); 128 | 129 | if ( notMatchedColumn != null ) { 130 | this.throwError( 131 | { 132 | title: this.errorTypes.configuration.title, 133 | text: 'Marker for column #'+notMatchedColumn+'# not found in row. At least one of listed selectors must return #'+notMatchedColumn+'# value.', 134 | pre : [ 135 | '$(\'[headers="'+notMatchedColumn+'"]\').text() //for nested reports embeded directly in Classic Report', 136 | '$(\'[headers="'+notMatchedColumn+'"]\').text() //for nested reports embeded directly in Interactive Report (column static ID is required)', 137 | '$(\'[headers="NR_'+this.inception.level+'_'+notMatchedColumn+'"]\').text() //for nested reports embeded in nested reports', 138 | '$(\'span[class*="'+notMatchedColumn+'"]\').text() //universal column marker (requires changes in APEX Column Formatting)' 139 | ], 140 | hints : [] 141 | } 142 | ); 143 | //this.destroy(); 144 | } 145 | 146 | if ( this.settings.isDefaultTemplate == true && this.settings.isDefaultTempateMarker == false ) { 147 | this.throwError( 148 | { 149 | title: this.errorTypes.configuration.title, 150 | text: 'Marker '+this.defaultTemplateMarker+' not found in definiton of the plugin "Default template HTML" attribute.', 151 | pre : [ 152 | 'Make sure the value of "Default template HTML" attribute contains at least '+this.defaultTemplateMarker+' marker."' 153 | ], 154 | hints : [] 155 | } 156 | ); 157 | 158 | } 159 | 160 | 161 | if ( this.options.forceToggle ) { 162 | this.toggle(); 163 | } 164 | 165 | }, 166 | // 167 | // 168 | _destroy: function(){ 169 | }, 170 | // 171 | // 172 | _setOption: function( pKey, pValue ) { 173 | if ( pKey === "value" ) { 174 | pValue = this._constrain( pValue ); 175 | } 176 | 177 | this._super( pKey, pValue ); 178 | }, 179 | options: function( pOptions ){ 180 | this._super( pOptions ); 181 | }, 182 | // 183 | // 184 | _setOptions: function( pOptions ) { 185 | this._super( pOptions ); 186 | }, 187 | // 188 | // nie uzywane 189 | setContentHere: function( pNode ){ 190 | this.nestedReport.contentHere = pNode; 191 | }, 192 | // 193 | // 194 | _notMatchedColumn: function(){ 195 | var 196 | headerTag, 197 | spanTag, 198 | column; 199 | 200 | for ( var i=0; i < this.queryColumnsNames.length; i++ ){ 201 | column = this.queryColumnsNames[i]; 202 | headerTag = this.row.find('[headers="'+column+'"]'); 203 | nestedHeaderTag = this.row.find('[headers="NR_'+this.inception.level+'_'+column+'"]'); 204 | spanTag = this.row.find('span[class*="'+column+'"]'); 205 | 206 | if ( headerTag.length == 0 && spanTag.length == 0 && nestedHeaderTag.length == 0) { 207 | return column; 208 | } 209 | else { 210 | if ( spanTag.length != 0 ) { 211 | this.queryColumnsValues.push( spanTag.first().text() ); 212 | } 213 | else if ( headerTag.length != 0 ) { 214 | this.queryColumnsValues.push( headerTag.first().text() ); 215 | 216 | } 217 | else { 218 | this.queryColumnsValues.push( nestedHeaderTag.first().text() ); 219 | } 220 | 221 | } 222 | 223 | } 224 | return null; 225 | }, 226 | // 227 | // 228 | _getPlugAttrFlag: function( pAttr, pExpectedValue ){ 229 | var pAttr = this.options.action['attribute'+pAttr]; 230 | 231 | if ( pAttr == undefined || pAttr == null ) { 232 | return false; 233 | } 234 | 235 | return pAttr.indexOf( pExpectedValue ) > -1; 236 | }, 237 | // 238 | // 239 | getParent: function(){ 240 | return this.inception; 241 | }, 242 | // 243 | // 244 | getLevel: function(){ 245 | return this.inception.level; 246 | }, 247 | // 248 | // 249 | _getExpandedFrom: function( pElement ) { 250 | var expanded; 251 | apex.debug.log('_getExpandedFrom', ', search in pElement =', pElement); 252 | 253 | expanded = pElement.find('*').filter( function(pIndex, pElement){ 254 | var self = $(pElement); 255 | 256 | if ( self.data('pretius-nestedReport') != undefined && self.nestedReport('isExpanded') == true ) { 257 | return true; 258 | } 259 | } ); 260 | 261 | apex.debug.log('_getExpandedFrom', ', found elements count = ', expanded.length, ', elements =', expanded); 262 | 263 | return expanded; 264 | }, 265 | // 266 | // 267 | _iAmYourFatherLuke: function(){ 268 | var 269 | closestContainer = this.td.closest('[class*='+this.classes.nestedReportTrContainer+']'), 270 | vader; 271 | 272 | if ( closestContainer.length > 0 ) { 273 | //na pewno jestem przynajmniej level 1 274 | vader = this._getExpandedFrom( closestContainer.prev() ); 275 | 276 | if ( vader != undefined && vader.length > 0 ) { 277 | return { 278 | 'type' : 'nested', 279 | 'element': vader, 280 | 'level' : (vader.nestedReport('getLevel'))+1 281 | }; 282 | } 283 | else { 284 | //first level 285 | return { 286 | 'type' : 'Invalid nested report', 287 | 'element': undefined, 288 | 'level' : -1 289 | 290 | }; 291 | } 292 | 293 | } 294 | //tu dorobic czy na pewno affectedElements jest najblizszym rodzicem 295 | else { 296 | return { 297 | 'type' : 'affectedElement', 298 | 'element': this.affectedElement, 299 | 'level' : 1 300 | 301 | }; 302 | } 303 | }, 304 | // 305 | // 306 | throwError: function( pError ){ 307 | var 308 | tr = this.createDefaultCallbackRow(), 309 | pre = $('
').append( JSON.stringify( pError, null, 2) ),
 310 |       duration = 400,
 311 |       closeDuration = 0,
 312 |       div = this._getErrorTemplate( pError ),
 313 |       closeBefore = this._expandedSisters();
 314 | 
 315 |     this.nestedReport.td        = tr.find('td');
 316 |     this.nestedReport.container = tr.find( '[class*='+this.classes.nestedReportDivContainer+']' );
 317 |     this.nestedReport.tr        = tr;
 318 | 
 319 |     closeBefore.nestedReport('collapse', closeDuration);  
 320 | 
 321 |     if ( closeBefore.length > 0 ) {
 322 |       closeDuration = this.settings.closeOtherDuration;
 323 |     }
 324 | 
 325 |     setTimeout( $.proxy(function(){
 326 |       this.nestedReport.container.html( div );
 327 |       this.nestedReport.tr.insertAfter( this.row );
 328 | 
 329 |       this.td.addClass( this.classes.tdExpanded );
 330 |       this.expanded = true;
 331 |       this.td.data('pretius-nestedReport-owner', this.element);
 332 | 
 333 |       this.changeBorderStyle();
 334 |       this.nestedReport.tr.show();
 335 | 
 336 |       if ( this.settings.isDefaultCallback && this.settings.isAddAnimation == false) {
 337 |         duration = 0;
 338 |       }
 339 | 
 340 |       this.nestedReport.container.slideDown( duration , $.proxy(function(){
 341 |         this.callbackExpanded();
 342 |       }, this));    
 343 |     }, this), closeDuration + 150);
 344 | 
 345 |     apex.debug.error(
 346 |       "Pretius APEX Nested Reports error:\n",
 347 |       "  "+pError.title.replace(/<[^>]+>/g, '')+"\n", 
 348 |       "    "+pError.text.replace(/<[^>]+>/g, '') 
 349 |     );
 350 | 
 351 |     if ( pError.hints.length > 0 ) {
 352 |       for ( var i = 0; i < pError.hints.length; i++ ) {
 353 |         apex.debug.warn(pError.hints[i].label+':', "\n\n"+pError.hints[i].value );
 354 |       }
 355 |     }
 356 | 
 357 |     if (  arguments.length > 1 ) {
 358 |       for ( var i = 1; i < arguments.length; i++ ) {
 359 |         apex.debug.warn('Additional info #'+(i-1)+':', arguments[i] );
 360 |       }
 361 |     }
 362 | 
 363 |     throw 'Plugin execution stopped.'
 364 |   },
 365 | //
 366 | //
 367 |   isAjaxRunning: function(){
 368 |     return this.ajax.running;
 369 |   },
 370 | //
 371 | //
 372 |   isExpanded: function(){
 373 |     return this.expanded;
 374 |   },
 375 | //
 376 | //
 377 |   toggle: function(){
 378 |     var
 379 |       closeDuration = 0,
 380 |       closeBefore;
 381 | 
 382 |     if ( this.expanded == true ) {
 383 |       this.collapse();
 384 |     }
 385 |     else {
 386 |       if ( this.settings.isCollapseExapnded == true) {
 387 |         closeBefore = this._expandedInReport();
 388 |       } else {
 389 |         closeBefore = this._expandedSisters();
 390 |       }
 391 | 
 392 |       if ( closeBefore.length > 0 ) {
 393 |         closeDuration = this.settings.closeOtherDuration;
 394 |         closeBefore.nestedReport('collapse', closeDuration);  
 395 | 
 396 |         setTimeout( $.proxy(function(){      
 397 |           this.show();
 398 |         }, this), closeDuration + 150);
 399 | 
 400 |       }
 401 |       else {
 402 |         this.show();
 403 |       }
 404 |     }
 405 |   },
 406 | 
 407 | //
 408 | //
 409 |   show: function(){
 410 |     
 411 |     if ( this.nestedReport.ajaxData == null ) {
 412 |       this.ajax.forced = true;
 413 |       this.ajaxFetchData();
 414 |       return void(0);
 415 |     }
 416 |     else {
 417 | 
 418 |       //dane są, sprawdz czy cache wlaczony
 419 |       if ( this.settings.isCacheResults == false && this.ajax.forced == false && this.ajax.isRefresh == false) {
 420 |         //zresetuj zawartość nested report, tak zeby wygenerowac go na nowo
 421 |         //this.nestedReport.tr.remove();
 422 |         this.nestedReport.tr = null;
 423 |         this.ajax.forced = true;
 424 |         this.ajaxFetchData();
 425 |         return;
 426 |       }
 427 |       else if ( this.settings.isCacheResults == false && this.ajax.forced == true && this.ajax.isRefresh == false) {
 428 |         this.ajax.forced = false;
 429 |       }
 430 |     }
 431 | 
 432 |     if ( this.settings.isDefaultCallback ) {
 433 |       this.doCallbackDefault();
 434 |     }
 435 |     else {
 436 |       this.doCallbackCustom()
 437 |     }
 438 |   },
 439 | //
 440 | //
 441 |   expand: function( pForceDuration ){
 442 |     var 
 443 |       duration = this.settings.animationTime;
 444 | 
 445 |     if ( this.settings.isAddAnimation == false) {
 446 |       duration = 0;
 447 |     }
 448 | 
 449 |     if ( pForceDuration != null && pForceDuration != undefined ) {
 450 |       duration = pForceDuration;      
 451 |     }
 452 | 
 453 |     this.animationRunning = true;
 454 |     //before slide down
 455 |     apex.event.trigger(this.affectedElement, 'pretius_default_callback', this.getEventData());
 456 | 
 457 |     //pretius-nestedReport-owner is used to find out triggering element
 458 |     //while scanning row or table for expanded cells
 459 |     this.td.data('pretius-nestedReport-owner', this.element);
 460 |     this.td.addClass( this.classes.tdExpanded );
 461 | 
 462 |     this.expanded = true;
 463 |     this.changeBorderStyle();
 464 |     
 465 |     this.nestedReport.tr.show();
 466 | 
 467 |     //after slide down
 468 |     this.nestedReport.container.slideDown( duration , $.proxy( this.callbackExpanded, this ));
 469 |   },
 470 | //
 471 | //
 472 |   collapse: function( pForceDuration ){
 473 |     var duration = pForceDuration == undefined ? this.settings.closeOtherDuration : pForceDuration;
 474 | 
 475 |     if ( this.settings.isAddAnimation == false) {
 476 |       duration = 0;
 477 |     }
 478 | 
 479 |     this.animationRunning = true;
 480 |     
 481 |     apex.event.trigger(this.affectedElement, 'pretius_default_callback', this.getEventData() );
 482 | 
 483 |     this.nestedReport.container.slideUp( duration , $.proxy( this.callbackCollapsed, this) );
 484 |   },
 485 | //
 486 | //
 487 |   doCallbackDefault: function(){
 488 |     var 
 489 |       newTr,
 490 |       nestedReportContent;
 491 | 
 492 |     if ( this.nestedReport.tr == null ) {
 493 |       // first drill down
 494 | 
 495 |       //create new nested report row
 496 |       newTr                       = this.createDefaultCallbackRow()
 497 |       //this.nestedReport.contentHere points td in newly created tr
 498 |       this.nestedReport.td        = newTr.find('td');
 499 |       this.nestedReport.container = newTr.find( '[class*='+this.classes.nestedReportDivContainer+']' );
 500 |       this.nestedReport.tr        = newTr;
 501 | 
 502 |       this.nestedReport.tr.insertAfter( this.row );
 503 | 
 504 |       if ( this.settings.isDefaultTemplate ) {
 505 | 
 506 |         nestedReportContent = this.renderTemplateDefault();
 507 | 
 508 |         this.nestedReport.contentHere.empty();
 509 |         this.nestedReport.contentHere.append( this.settings.defaultTemplateBefore );
 510 |         this.nestedReport.contentHere.append( nestedReportContent );
 511 |         this.nestedReport.contentHere.append( this.settings.defaultTemplateAfter );
 512 | 
 513 |       }
 514 |       else {
 515 |         nestedReportContent = this.renderTemplateCustom();
 516 |         this.nestedReport.contentHere.html( nestedReportContent )
 517 |       }
 518 | 
 519 |     }
 520 |     else {
 521 |       // next drill down, the content is only replaced when caching is turned off
 522 | 
 523 |       if ( this.ajax.isRefresh ) {
 524 |         //this.ajax.isRefresh = false;
 525 | 
 526 |         this.nestedReport.contentHere.empty();
 527 | 
 528 |         if ( this.settings.isDefaultTemplate ) {
 529 |           nestedReportContent = this.renderTemplateDefault();
 530 | 
 531 |           this.nestedReport.contentHere.append( this.settings.defaultTemplateBefore );
 532 |           this.nestedReport.contentHere.append( nestedReportContent );
 533 |           this.nestedReport.contentHere.append( this.settings.defaultTemplateAfter );
 534 |         }
 535 |         else {
 536 |           nestedReportContent = this.renderTemplateCustom();
 537 |           this.nestedReport.contentHere.html( nestedReportContent )
 538 |         }
 539 |        
 540 |       }
 541 |       else {
 542 |         //do nothing, the content is already rendered
 543 |         null;
 544 |       }
 545 |     }
 546 | 
 547 | 
 548 |     //bind nested report with manualy triggered event from APEX
 549 |     //ex: $(this.triggeringElement).trigger('nestedreportrefresh');
 550 |     this.nestedReport.tr.off('nestedreportrefresh').on('nestedreportrefresh', $.proxy( this.defaultCallbackEvent_refresh, this ) );
 551 | 
 552 |     //bind anchors with class ".nestedreport--refresh" to refresh content of nested report
 553 |     this.nestedReport.tr.off('click', '.nestedreport--refresh')   .on('click', '.nestedreport--refresh', $.proxy( this.defaultCallbackEvent_refresh, this ) );
 554 |     //bind anchors with class ".nestedreport--slideup" to manualy 
 555 |     //collapse nested report
 556 |     this.nestedReport.tr.off('click', '.nestedreport--slideup')   .on('click', '.nestedreport--slideup', $.proxy( this.defaultCallbackEvent_slideup, this ) );    
 557 |     //bind anchors with class ".nestedreport--slideupAll" to manualy 
 558 |     //collapse all nested reports
 559 |     this.nestedReport.tr.off('click', '.nestedreport--slideupAll').on('click', '.nestedreport--slideupAll', $.proxy( this.defaultCallbackEvent_slideupAll, this ) );
 560 |     //bind anchors with class ".nestedreport--expandAll" to manualy 
 561 |     //expand all nested reports matched with given selector as anchor attribute
 562 |     this.nestedReport.tr.off('click', '.nestedreport--expandAll').on('click', '.nestedreport--expandAll', $.proxy( this.defaultCallbackEvent_expandAll, this ) );        
 563 | 
 564 |     this.expand();
 565 |     
 566 |   },
 567 | //
 568 |   defaultCallbackEvent_expandAll: function( pEvent ){
 569 |     var 
 570 |       anchor   = $(pEvent.target),
 571 |       selector = anchor.attr('selector'),
 572 |       toBeExpanded = this.nestedReport.tr.find( selector );
 573 | 
 574 |     pEvent.stopPropagation();
 575 |     pEvent.preventDefault();
 576 | 
 577 |     //filter to only selectors from this nested report
 578 |     toBeExpanded = toBeExpanded.filter( $.proxy(function(pIndex, pElem){
 579 |       var closestNestedContainer = $(pElem).closest('[class*='+this.classes.nestedReportDivContainer+']')
 580 |       return closestNestedContainer.get(0) == this.nestedReport.container.get(0);
 581 |     }, this) );
 582 | 
 583 |     toBeExpanded.each( $.proxy( function( pIdx, pElem ){
 584 |       var self = $(pElem);
 585 | 
 586 |       if ( self.data('pretius-nestedReport') == undefined ) {
 587 |         self.trigger('click');
 588 |       }
 589 |       else if ( self.nestedReport('isExpanded') == false ) {
 590 |         self.nestedReport('expand');
 591 |       }
 592 |     }, this) );
 593 |     
 594 |   },
 595 | //
 596 | //
 597 |   defaultCallbackEvent_slideupAll: function( pEvent ){
 598 |     var 
 599 |       anchor              = $(pEvent.target),
 600 |       anchorAttrDuration  = anchor.attr('duration') == undefined  ? this.settings.animationTime : parseInt( anchor.attr('duration') ),
 601 |       duration            = isNaN(anchorAttrDuration)             ? this.settings.animationTime : anchorAttrDuration,
 602 |       expanded            = this._getExpandedFrom( this.nestedReport.tr );
 603 | 
 604 |     pEvent.stopPropagation();
 605 |     pEvent.preventDefault();
 606 | 
 607 |     expanded.nestedReport('collapse', duration);
 608 |   },
 609 | //
 610 | //  
 611 |   defaultCallbackEvent_slideup: function( pEvent ){
 612 |     var 
 613 |       anchor              = $(pEvent.target),
 614 |       anchorAttrDuration  = anchor.attr('duration') == undefined  ? this.settings.animationTime : parseInt( anchor.attr('duration') ),
 615 |       duration            = isNaN(anchorAttrDuration)             ? this.settings.animationTime : anchorAttrDuration;
 616 | 
 617 |     pEvent.stopPropagation();
 618 |     pEvent.preventDefault();
 619 |     
 620 |     this.collapse( duration );
 621 |   },
 622 | //
 623 | //
 624 |   defaultCallbackEvent_refresh: function( pEvent ){
 625 |     pEvent.stopPropagation();
 626 |     pEvent.preventDefault();
 627 |     
 628 |     this.ajaxFetchData( true );
 629 |   },
 630 | 
 631 |   doCallbackCustom: function(){
 632 |     var
 633 |       functionBody = "                                       \n"+
 634 |         "this.callback = {                                   \n"+
 635 |         "  'sqlResultObj'      : data,                       \n"+
 636 |         "  'triggeringElement' : $(da.triggeringElement),    \n"+
 637 |         "  'affactedReport'    : $(da.affectedElements[0]),  \n"+
 638 |         "  'renderedTemplate'  : templateContent,            \n"+
 639 |         "  'browserEvent'      : da.browserEvent,            \n"+
 640 |         "  //newly added in v1.1                             \n"+
 641 |         "  'dynamicAction'     : da,                         \n"+
 642 |         "  'pluginSettings'    : settings                    \n"+
 643 |         "};                                                  \n"+
 644 |         "//start of custom callback javascript               \n"+
 645 |         this.settings.customCallbackJs                     +"\n"+
 646 |         "//end of custom callback javascript                 \n",
 647 | 
 648 |       tempFunc = new Function("templateContent", "settings", "da", "data", functionBody),
 649 |       template;
 650 | 
 651 |     if ( this.settings.isDefaultTemplate ) {
 652 |       template = $('
').addClass( this.classes.nestedReportDivContainer ).append( this.renderTemplateDefault() ); 653 | } 654 | else { 655 | template = this.renderTemplateCustom(); 656 | } 657 | 658 | try { 659 | tempFunc( template, this.settings, this.options, this.nestedReport.ajaxData ); 660 | } catch( thrownError ) { 661 | 662 | this.throwError( 663 | { 664 | title: this.errorTypes.customFunction.title, 665 | text : 'While executing Custom Callback JavaScript error occured', 666 | pre : [thrownError], 667 | hints : [ 668 | this._hint( 'Custom callback JavaScript', tempFunc.toString() ) 669 | ] 670 | } 671 | ); 672 | 673 | } 674 | }, 675 | // 676 | // 677 | _ajaxStart: function(){ 678 | this.ajax.running = true; 679 | 680 | if ( this.settings.isLoadingIndicator ) { 681 | this._showSpinner(); 682 | } 683 | 684 | }, 685 | // 686 | // 687 | _ajaxEnd: function(){ 688 | this.ajax.running = false; 689 | this._hideSpinner(); 690 | 691 | }, 692 | // 693 | // 694 | _ajaxSuccess: function(pData, pTextStatus, pJqXHR){ 695 | this._ajaxEnd(); 696 | 697 | for ( var i=0; i < pData.data.length; i++ ) { 698 | pData.data[i].rowClass = i % 2 == 0 ? 'odd' : 'even'; 699 | } 700 | 701 | this.nestedReport.ajaxData = pData; 702 | this.show(); 703 | }, 704 | // 705 | // 706 | _hint: function( pTitle, pValue ) { 707 | return { 708 | label: pTitle, 709 | value: pValue 710 | } 711 | }, 712 | _ajaxError: function( pJqXHR, pTextStatus, pErrorThrown ){ 713 | this._ajaxEnd(); 714 | 715 | if ( pTextStatus == 'parsererror' ) { 716 | this.throwError({ 717 | title: this.errorTypes.ajax.title, 718 | text : 'Ajax response could not be parsed as JSON', 719 | pre : pErrorThrown.message, 720 | hints : [ 721 | this._hint( 'Ajax response text', pJqXHR.responseText ) 722 | ] 723 | }); 724 | return void(0); 725 | } 726 | 727 | this.throwError({ 728 | title: this.errorTypes.ajax.title, 729 | text : pJqXHR.responseJSON.addInfo, 730 | pre : pJqXHR.responseJSON.error, 731 | hints : [ 732 | this._hint( 'Ajax JSON', pJqXHR.responseJSON ), 733 | this._hint( 'Ajax error info', pJqXHR.responseJSON.addInfo ), 734 | this._hint( 'Ajax error thrown', pErrorThrown ) 735 | ] 736 | }); 737 | }, 738 | // 739 | // 740 | ajaxFetchData: function( pIsRefresh ){ 741 | var 742 | pAjaxCallbackName = this.ajax.id, 743 | pData = { 744 | //type : "GET", 745 | //dataType : "json", 746 | x01 : this.queryColumnsNames.join(':'), //nazwy kolumn 747 | x02 : this.queryColumnsValues.join(':') //wartości kolumn 748 | }, 749 | pOptions; 750 | 751 | this.ajax.isRefresh = pIsRefresh == undefined ? false : pIsRefresh; 752 | 753 | pOptions = { 754 | success : $.proxy(this._ajaxSuccess, this), 755 | error : $.proxy(this._ajaxError , this) 756 | }; 757 | 758 | if ( !this.isAjaxRunning() ) { 759 | this._ajaxStart(); 760 | 761 | if ( this.queryItems.length > 0 ) { 762 | pData.pageItems = '#'+this.queryItems.join(',#'); 763 | } 764 | 765 | apex.server.plugin ( pAjaxCallbackName, pData, pOptions ); 766 | } 767 | }, 768 | // 769 | // 770 | _ajaxCreateIndicatorTdIcon: function(){ 771 | var 772 | icon = $(''), 773 | div = $('
'); 774 | 775 | if ( this.settings.isSpinnerTdIcon ) { 776 | div.addClass( this.classes.ajaxIndicatorRight ); 777 | } 778 | else if ( this.settings.isSpinnerTdContent ) { 779 | div.addClass( this.classes.ajaxIndicatorContent ); 780 | } 781 | 782 | div.append(icon); 783 | return div; 784 | }, 785 | // 786 | // 787 | _showSpinner: function(){ 788 | 789 | if ( this.settings.isSpinnerTdIcon ) { 790 | this.spinner = this._ajaxCreateIndicatorTdIcon(); 791 | this.td.append( this.spinner ); 792 | } 793 | else if ( this.settings.isSpinnerReport ) { 794 | this.spinner = apex.util.showSpinner( this.table ); 795 | } 796 | else if ( this.settings.isSpinnerTdCell ) { 797 | this.spinner = apex.util.showSpinner( this.td ); 798 | } 799 | else if ( this.settings.isSpinnerTdContent ) { 800 | this.spinner = this._ajaxCreateIndicatorTdIcon(); 801 | if ( this.td.children().length > 0 ) { 802 | this.td.data('pretius-nestedReport-content', this.td.children().detach() ); 803 | } 804 | else { 805 | this.td.data('pretius-nestedReport-content', this.td.text() ); 806 | } 807 | 808 | this.td.html( this.spinner ); 809 | } 810 | else { 811 | 812 | return this.throwError({ 813 | title: this.errorTypes.configuration.title, 814 | text : 'Unknown spinner option', 815 | pre : ['attribute12: '+this.options.action.attribute12], 816 | hints : [], 817 | 818 | addInfo: 'Unknown spinner option', 819 | error: 'attribute12: '+this.options.action.attribute12 820 | }, this.errorTypes.configuration ); 821 | 822 | } 823 | }, 824 | // 825 | // 826 | _hideSpinner: function(){ 827 | if ( this.settings.isLoadingIndicator ) { 828 | this.spinner.fadeOut(400, $.proxy(function(){ 829 | this.spinner.remove(); 830 | 831 | if ( this.settings.isSpinnerTdContent ) { 832 | this.td.html( this.td.data('pretius-nestedReport-content') ) 833 | } 834 | }, this)); 835 | } 836 | }, 837 | 838 | // 839 | // 840 | renderTemplateCustom: function(){ 841 | var 842 | template = this.settings.customTemplate, 843 | rendered, 844 | dataObject = this.nestedReport.ajaxData, 845 | error = { 846 | addInfo: null, 847 | error: null 848 | }, 849 | rendered, 850 | errorText; 851 | 852 | if ( dataObject.data.length == 0 ) { 853 | return this._renderNoDataFound(); 854 | } 855 | 856 | try { 857 | rendered = Mustache.render( template, dataObject); 858 | } catch( error ) { 859 | 860 | error.addInfo = 'While rendering custom template unexpected error occured: '; 861 | error.error = error; 862 | return this._getErrorTemplate( error, 'configuration' ); 863 | } 864 | 865 | return rendered; 866 | }, 867 | // 868 | // 869 | _renderNoDataFound: function(){ 870 | return '
'+this.settings.noDataFound+'
' 871 | }, 872 | // 873 | // 874 | getTemplateDefaultBody: function(){ 875 | var 876 | dataObject = this.nestedReport.ajaxData, 877 | td_row_template = '', 878 | level = this.inception.level; 879 | 880 | 881 | for ( var i = 0; i < dataObject.headers.length; i++ ) { 882 | td_row_template += '' + 883 | '' + 889 | '{{{'+ dataObject.headers[i].COLUMN_NAME +'}}}' + 890 | ''; 891 | } 892 | 893 | td_row_template = '{{#data}}'+td_row_template+'{{/data}}'; 894 | return td_row_template; 895 | }, 896 | // 897 | // 898 | getTemplateDefault: function(){ 899 | var 900 | th_row_template = '', 901 | dataObject = this.nestedReport.ajaxData, 902 | template, 903 | headerHtml, 904 | headerArr, 905 | isHeaderIcon = false, 906 | tableClass = this.settings.isStrechReport ? this.classes.nestedReportTable+' '+this.classes.tableStrechReport : this.classes.nestedReportTable; 907 | 908 | for ( var i = 0; i < dataObject.headers.length; i++ ) { 909 | //icons fa-iconname 910 | if ( /^derivied[0-9]{1}[0-9]{1}_fa_[a-z]{3,}$/gi.test( dataObject.headers[i].COLUMN_NAME.toLowerCase()) ) { 911 | headerArr = dataObject.headers[i].COLUMN_NAME.split('_'); 912 | headerHtml = ''; 913 | isHeaderIcon = true; 914 | } 915 | //icons fa-iconname-morename 916 | else if (/^derivied[0-9]{1}[0-9]{1}_fa_[a-z]{3,}_[a-z]{3,}$/gi.test( dataObject.headers[i].COLUMN_NAME.toLowerCase()) ) { 917 | headerArr = dataObject.headers[i].COLUMN_NAME.split('_'); 918 | headerHtml = ''; 919 | isHeaderIcon = true; 920 | } 921 | else if ( /^derivied[0-9]{1}[0-9]{1}_empty$/gi.test( dataObject.headers[i].COLUMN_NAME.toLowerCase()) ) { 922 | headerHtml = ''; 923 | isHeaderIcon = true; 924 | } 925 | else { 926 | isHeaderIcon = false; 927 | headerHtml = dataObject.headers[i].COLUMN_NAME; 928 | } 929 | 930 | if ( this.settings.isSortingSupported && isHeaderIcon == false ) { 931 | th_row_template += ''+ 932 | ' '+ 934 | '
'+ 935 | ' '+ 936 | ' '+ headerHtml +' '+ 937 | ' '+ 938 | ' '+ 939 | ' '+ 940 | '
'+ 941 | ''; 942 | } 943 | else { 944 | th_row_template += ''+ headerHtml +''; 947 | } 948 | } 949 | 950 | th_row_template = ''+th_row_template+''; 951 | 952 | template = ''+th_row_template+''+ this.getTemplateDefaultBody() +'
'; 953 | return template; 954 | 955 | }, 956 | // 957 | // Used by this.sort function to render tbody of nested report 958 | renderTemplateDefaultBody: function(){ 959 | return Mustache.render( this.getTemplateDefaultBody(), this.nestedReport.ajaxData ); 960 | }, 961 | // 962 | // 963 | renderTemplateDefault: function(){ 964 | var 965 | content; 966 | 967 | if ( this.nestedReport.ajaxData.data.length == 0 ) { 968 | content = $(this._renderNoDataFound()); 969 | } 970 | else { 971 | content = Mustache.render( this.getTemplateDefault(), this.nestedReport.ajaxData ); 972 | content = $(content); 973 | 974 | content.css({ 975 | 'backgroundColor': this.settings.bgColor 976 | }); 977 | 978 | content.find('th a').bind('click', $.proxy(this.sort, this)); 979 | } 980 | 981 | return content; 982 | }, 983 | // 984 | // 985 | createDefaultCallbackRow: function(){ 986 | //do rozwazenia, nadawanie unikalnego ID 987 | var 988 | tr = $(''), 989 | td = $(''), 990 | divInTd = $('
'), 991 | divTdOverflow = $('
'); 992 | 993 | //jesli wykorzystuje maxHeight to ustaw maxHeight oraz ustaw referencje gdzie pisać 994 | if ( this.settings.isSetMaxHeight ) { 995 | divTdOverflow.addClass( this.classes.nestedReportOverflowContainer ) 996 | divTdOverflow.css('maxHeight', this.settings.maxHeight); 997 | divTdOverflow.appendTo( divInTd ); 998 | this.nestedReport.contentHere = divTdOverflow; 999 | } 1000 | else { 1001 | this.nestedReport.contentHere = divInTd; 1002 | } 1003 | 1004 | tr.attr('nested-level', this.inception.level ); 1005 | tr.addClass( this.classes.nestedReportTrContainer ); 1006 | td.attr('colspan', this.row.find('td').length); 1007 | 1008 | divInTd.addClass( this.classes.nestedReportDivContainer ); 1009 | 1010 | td.on('mouseenter', $.proxy(this._highlight,this)) 1011 | td.on('mouseleave', $.proxy(this._removeHighlight,this)); 1012 | 1013 | td.append( divInTd ); 1014 | tr.append( td ); 1015 | 1016 | return tr; 1017 | }, 1018 | // 1019 | // 1020 | _getErrorTemplate: function( pError ){ 1021 | var 1022 | title, div; 1023 | 1024 | 1025 | if ( pError.pre instanceof Array ) { 1026 | pError.pre = pError.pre.join("\n"); 1027 | pError.pre = pError.pre.replace(//gi, '>'); 1028 | } 1029 | else if ( pError.pre instanceof Object ) { 1030 | pError.pre = JSON.stringify( pError.pre, null, 2 ).replace(//gi, '>'); 1031 | } 1032 | else if ( typeof pError.pre == "string") { 1033 | pError.pre = pError.pre.replace(//gi, '>'); 1034 | } 1035 | else { 1036 | apex.debug.info('_getErrorTemplate type of pError.pre:', (typeof pError.pre) ) 1037 | } 1038 | 1039 | //zrobic bardziej czytelne 1040 | div = $(''+ 1041 | '
'+ 1042 | '
'+ 1043 | ' '+ 1044 | ' '+pError.title+' '+ 1045 | '
'+ 1046 | '
'+ 1047 | ' '+pError.text+' '+ 1048 | '
'+ 1049 | '
'+ 1050 | '
'+pError.pre+'
'+ 1051 | '
'+ 1052 | '
'+ 1053 | ''); 1054 | 1055 | return div; 1056 | }, 1057 | 1058 | // 1059 | // 1060 | _expandedInReport: function(){ 1061 | var expandedElements = $(); 1062 | 1063 | this.table.find('td').each( function(){ 1064 | var 1065 | self = $(this), 1066 | tdOwner = self.data('pretius-nestedReport-owner'); 1067 | 1068 | if ( tdOwner != undefined && tdOwner.nestedReport('isExpanded') == true ) { 1069 | expandedElements = expandedElements.add( tdOwner ); 1070 | } 1071 | 1072 | 1073 | } ); 1074 | 1075 | return expandedElements; 1076 | }, 1077 | // 1078 | // 1079 | _expandedSisters: function(){ 1080 | var expandedElements = $(); 1081 | 1082 | this.row.find('td').each( function(){ 1083 | var 1084 | self = $(this), 1085 | tdOwner = self.data('pretius-nestedReport-owner'); 1086 | 1087 | if ( tdOwner != undefined && tdOwner.nestedReport('isExpanded') == true ) { 1088 | expandedElements = expandedElements.add( tdOwner ); 1089 | } 1090 | } ); 1091 | 1092 | return expandedElements; 1093 | }, 1094 | // 1095 | // 1096 | sort: function( pEvent ){ 1097 | var 1098 | anchor = $(pEvent.currentTarget), 1099 | th = anchor.closest('th'), 1100 | otherTh = th.prevAll().add( th.nextAll() ), 1101 | div = th.find('.u-Report-sort'), 1102 | headerText = anchor.text(); 1103 | 1104 | //apex.debug.log('sort', 'nestedReportData', this.nestedReport.ajaxData); 1105 | 1106 | otherTh.find('.u-Report-sort').removeClass('sort--desc sort--asc sort'); 1107 | 1108 | if ( !div.is('.sort') || div.is('.sort--asc')) { 1109 | this.nestedReport.ajaxData.data.sort(this._sortFunc( headerText )); 1110 | this.nestedReport.ajaxData.data.reverse(); 1111 | div.removeClass('sort--asc').addClass( 'sort sort--desc' ); 1112 | } 1113 | else { 1114 | this.nestedReport.ajaxData.data.sort(this._sortFunc( headerText )); 1115 | div.removeClass('sort--desc').addClass( 'sort sort--asc' ); 1116 | } 1117 | 1118 | this.nestedReport.contentHere.find('tbody').html( this.renderTemplateDefaultBody() ); 1119 | }, 1120 | // 1121 | // 1122 | _sortFunc: function(pProperty) { 1123 | var sortOrder = 1; 1124 | 1125 | if(pProperty[0] === "-") { 1126 | sortOrder = -1; 1127 | pProperty = pProperty.substr(1); 1128 | } 1129 | 1130 | return function (a,b) { 1131 | var result = (a[pProperty] < b[pProperty]) ? -1 : (a[pProperty] > b[pProperty]) ? 1 : 0; 1132 | return result * sortOrder; 1133 | } 1134 | }, 1135 | // 1136 | // 1137 | _removeHighlight: function( pEvent ){ 1138 | pEvent.stopImmediatePropagation(); 1139 | 1140 | if ( this.expanded ) { 1141 | this.td.css( 'backgroundColor', this.settings.bgColor ) 1142 | this._forceBackgroundColor( this.td ); 1143 | 1144 | this.nestedReport.td.css( 'backgroundColor', this.settings.bgColor ); 1145 | 1146 | 1147 | this.row.removeClass('pretius--hover'); 1148 | //blad gdy wylaczony cache i nastepuja proba odswiezenia nested report z poziomu nested report 1149 | this.nestedReport.tr.removeClass('pretius--hover'); 1150 | } 1151 | }, 1152 | // 1153 | // 1154 | _highlight: function( pEvent ){ 1155 | pEvent.stopImmediatePropagation(); 1156 | 1157 | if ( this.expanded ) { 1158 | this.td.css( 'backgroundColor', this.settings.BgColorhighlight ); 1159 | //its needed to override !important from APEX theme css 1160 | this._forceBackgroundColor( this.td ); 1161 | 1162 | this.nestedReport.td.css( 'backgroundColor', this.settings.BgColorhighlight ); 1163 | 1164 | this.row.addClass('pretius--hover'); 1165 | this.nestedReport.tr.addClass('pretius--hover'); 1166 | } 1167 | }, 1168 | // 1169 | // 1170 | changeBorderStyle: function(){ 1171 | var 1172 | otherTds = this.td.prevAll('td').add( this.td.nextAll('td') ), 1173 | borderStyle = 'solid', 1174 | borderWidth = '1px', 1175 | borderColor = this.settings.borderColor, 1176 | bgColor = this.settings.bgColor; 1177 | 1178 | if ( this.expanded ) { 1179 | this.td.css({ 1180 | 'border-left' : borderWidth+' '+borderStyle+' '+borderColor, 1181 | 'border-top' : borderWidth+' '+borderStyle+' '+borderColor, 1182 | 'border-right' : borderWidth+' '+borderStyle+' '+borderColor, 1183 | 'border-bottom' : borderWidth+' '+borderStyle+' '+bgColor, 1184 | 'backgroundColor': bgColor 1185 | }); 1186 | 1187 | this.nestedReport.td.css({ 1188 | 'backgroundColor': bgColor, 1189 | 'borderLeft' : borderWidth+' '+borderStyle+' '+borderColor, 1190 | 'borderRight' : borderWidth+' '+borderStyle+' '+borderColor, 1191 | 'borderBottom' : borderWidth+' '+borderStyle+' '+borderColor 1192 | }); 1193 | 1194 | otherTds.css('border-bottom', borderWidth+' '+borderStyle+' '+borderColor); 1195 | } 1196 | else { 1197 | this.td.css({ 1198 | 'border-left' : '', 1199 | 'border-top' : '', 1200 | 'border-right' : '', 1201 | 'border-bottom' : '', 1202 | 'backgroundColor': '' 1203 | }); 1204 | 1205 | if ( this.nestedReport.tr.is(':last-child') ) { 1206 | this.nestedReport.tr.show(); 1207 | this.nestedReport.td.css('borderColor', this.td.css('borderTopColor')); 1208 | } 1209 | 1210 | otherTds.css('border-bottom', ''); 1211 | } 1212 | }, 1213 | // 1214 | // 1215 | _overrideApexTrHover: function( pFlag ){ 1216 | if ( this.isExpanded() ) { 1217 | //ustaw kolor jak z konfiguracji 1218 | this.td.css( 'backgroundColor', this.settings.bgColor ); 1219 | //its needed to override !important from APEX theme css 1220 | this._forceBackgroundColor( this.td ); 1221 | } 1222 | }, 1223 | // 1224 | // 1225 | _forceBackgroundColor: function( pElem ) { 1226 | var 1227 | styles = pElem.attr('style'), 1228 | arr = styles.split(';'); 1229 | 1230 | for (var idx in arr) { 1231 | if ( arr[idx].indexOf('background-color') > -1 ) { 1232 | arr[idx] += ' !important' 1233 | } 1234 | } 1235 | 1236 | pElem.attr('style', arr.join(';')); 1237 | }, 1238 | // 1239 | // 1240 | callbackExpanded: function(){ 1241 | this.animationRunning = false; 1242 | apex.event.trigger(this.affectedElement, 'pretius_default_callback', this.getEventData()); 1243 | }, 1244 | // 1245 | // 1246 | getEventData: function(){ 1247 | var 1248 | isAfterRefresh = false, 1249 | returnObject; 1250 | 1251 | //after refresh but animation is in progress (duration = 0) 1252 | if ( this.ajax.isRefresh == true && this.animationRunning == true && this.expanded == true ) { 1253 | isAfterRefresh = true; 1254 | } 1255 | //after refresh byt row is fully expanded 1256 | else if ( this.ajax.isRefresh == true && this.animationRunning == false && this.expanded == true ) { 1257 | isAfterRefresh = true; 1258 | this.ajax.isRefresh = false; 1259 | } 1260 | 1261 | returnObject = { 1262 | 'isCollapsing' : this.animationRunning == true && this.expanded == true ? true : false, 1263 | 'isCollapsed' : this.animationRunning == false && this.expanded == false ? true : false, 1264 | 'isExpanding' : this.animationRunning == true && this.expanded == false ? true : false, 1265 | 'isExpanded' : this.animationRunning == false && this.expanded == true ? true : false, 1266 | 'animationRunning' : this.animationRunning, 1267 | 'afterRefresh' : isAfterRefresh, 1268 | //'plugin' : this, 1269 | 'report' : this.affectedElement, 1270 | 'triggeringTd' : this.td, 1271 | 'triggeringElement' : this.element, 1272 | 'nestedReportRow' : this.nestedReport.tr, 1273 | 'nestedReportData' : this.nestedReport.ajaxData, 1274 | 'parent' : this.inception 1275 | }; 1276 | 1277 | 1278 | return returnObject; 1279 | }, 1280 | // 1281 | // 1282 | callbackCollapsed: function(){ 1283 | this.td.removeClass( this.classes.tdExpanded ); 1284 | this.expanded = false; 1285 | 1286 | this.nestedReport.tr.hide(); 1287 | this.changeBorderStyle(); 1288 | 1289 | if ( this.settings.isCacheResults == false) { 1290 | this.nestedReport.tr.remove(); 1291 | } 1292 | this.animationRunning = false; 1293 | apex.event.trigger(this.affectedElement, 'pretius_default_callback', this.getEventData()); 1294 | } 1295 | }); -------------------------------------------------------------------------------- /server/pretius_row_details_styles.css: -------------------------------------------------------------------------------- 1 | 2 | td > .rowDetailsContainer { 3 | display: none; 4 | padding: 10px; 5 | } 6 | 7 | td > .rowDetailsContainer > .overflow { 8 | overflow: auto; 9 | } 10 | 11 | /* 12 | div.rowDetailsContainer table { 13 | background-color: red; 14 | } 15 | */ 16 | 17 | .pretius--ajaxIndicator.floatRight { 18 | float:right; 19 | opacity: 0.5; 20 | } 21 | 22 | .pretius--ajaxIndicator.content { 23 | /*text-align:center;*/ 24 | opacity: 0.5; 25 | } 26 | 27 | div.pretius--error { 28 | text-align:center; 29 | } 30 | 31 | .pretius--reason { 32 | 33 | } 34 | 35 | .pretius--reason .fa { 36 | font-size: 30px; 37 | line-height: 40px; 38 | display: block; 39 | opacity: 0.7; 40 | } 41 | 42 | .pretius--errorTitle { 43 | font-size: 20px; 44 | line-height: 30px; 45 | } 46 | 47 | .pretius--techError pre, 48 | .pretius--errorAddInfo { 49 | margin: 0px; 50 | line-height: 30px; 51 | font-size:14px; 52 | } 53 | 54 | .pretius--techError pre { 55 | line-height: 16px; 56 | text-align:left; 57 | background-color: rgba(255,255,255, 0.4); 58 | border: 1px dashed rgba(0,0,0,0.3); 59 | padding: 2px 10px; 60 | } 61 | 62 | div.rowDetailsContainer .u-Report-sort.sort--asc .icon-rpt-sort-asc { 63 | display: inline-block; 64 | } 65 | 66 | div.rowDetailsContainer .u-Report-sort.sort--desc .icon-rpt-sort-desc { 67 | display: inline-block; 68 | } 69 | 70 | div.rowDetailsContainer .u-Report-sortIcon { 71 | display: none; 72 | } 73 | 74 | .t-Report-report div.rowDetailsContainer .t-Report-report.pretius--strechReport, 75 | .a-IRR-table div.rowDetailsContainer .t-Report-report.pretius--strechReport, 76 | .t-Report-report.pretius--strechReport { 77 | width: 100%; 78 | } 79 | 80 | .t-Report-report div.rowDetailsContainer .t-Report-report { 81 | width: auto; 82 | } 83 | 84 | /* Default template / border color */ 85 | div.rowDetailsContainer td.t-Report-cell:last-child:not(.pretius--expanded), 86 | div.rowDetailsContainer th.t-Report-colHead:last-child, 87 | div.rowDetailsContainer td.t-Report-cell:not(.pretius--expanded), 88 | div.rowDetailsContainer th.t-Report-colHead { 89 | /*border-color: rgba(0,0,0, 0.1) !important;*/ 90 | border-color: #dcdcdc; 91 | } 92 | 93 | /* Default template / odd row cells background */ 94 | tr.pretius--hover div.rowDetailsContainer table.t-Report-report > tbody > tr.odd td.t-Report-cell:not(.pretius--expanded), 95 | div.rowDetailsContainer table.t-Report-report > tbody > tr.odd td.t-Report-cell:not(.pretius--expanded) { 96 | /*background-color: rgba(255, 255, 255, 0.3) !important;*/ 97 | background-color: #f7f7f7 !important; 98 | } 99 | /* Default template / even row cells background */ 100 | tr.pretius--hover div.rowDetailsContainer table.t-Report-report > tbody > tr.even td.t-Report-cell:not(.pretius--expanded), 101 | div.rowDetailsContainer table.t-Report-report > tbody > tr.even td.t-Report-cell:not(.pretius--expanded) { 102 | /*background-color: rgba(255, 255, 255, 0.6) !important;*/ 103 | background-color: #fcfcfc !important; 104 | } 105 | 106 | /* Default template / highlight cell color */ 107 | tr.pretius--hover div.rowDetailsContainer table.t-Report-report > tbody > tr.odd:hover td.t-Report-cell:not(.pretius--expanded), 108 | tr.pretius--hover div.rowDetailsContainer table.t-Report-report > tbody > tr.even:hover td.t-Report-cell:not(.pretius--expanded) { 109 | /*background-color: rgba(255, 255, 255, 0.8) !important;*/ 110 | background-color: #f0f0f0 !important; 111 | } 112 | 113 | /* Default template / heading background color */ 114 | div.rowDetailsContainer th.t-Report-colHead { 115 | /*background-color: rgba(255, 255, 255, 0.8);*/ 116 | background-color: #fff !important; 117 | } 118 | 119 | /* IR */ 120 | .a-IRR-table div.rowDetailsContainer tr td:first-child, 121 | .a-IRR-table div.rowDetailsContainer tr th:first-child { 122 | border-left-width: 1px; 123 | } -------------------------------------------------------------------------------- /src/PRETIUS_APEX_NESTED_REPORTS.plb: -------------------------------------------------------------------------------- 1 | create or replace package body "PRETIUS_APEX_NESTED_REPORTS" is 2 | 3 | ------------------------ 4 | function printAttributes( 5 | p_dynamic_action_render_result in apex_plugin.t_dynamic_action_render_result 6 | ) return clob is 7 | 8 | begin 9 | 10 | apex_json.initialize_clob_output; 11 | 12 | apex_json.open_object; 13 | apex_json.write( 'type', 'apex_plugin.t_dynamic_action_render_result' ); 14 | 15 | apex_json.write( 'javascript_function' , p_dynamic_action_render_result.javascript_function ); 16 | apex_json.write( 'ajax_identifier' , p_dynamic_action_render_result.ajax_identifier ); 17 | apex_json.write( 'attribute_01' , p_dynamic_action_render_result.attribute_01 ); 18 | apex_json.write( 'attribute_02' , p_dynamic_action_render_result.attribute_02 ); 19 | apex_json.write( 'attribute_03' , p_dynamic_action_render_result.attribute_03 ); 20 | apex_json.write( 'attribute_04' , p_dynamic_action_render_result.attribute_04 ); 21 | apex_json.write( 'attribute_05' , p_dynamic_action_render_result.attribute_05 ); 22 | apex_json.write( 'attribute_06' , p_dynamic_action_render_result.attribute_06 ); 23 | apex_json.write( 'attribute_07' , p_dynamic_action_render_result.attribute_07 ); 24 | apex_json.write( 'attribute_08' , p_dynamic_action_render_result.attribute_08 ); 25 | apex_json.write( 'attribute_09' , p_dynamic_action_render_result.attribute_09 ); 26 | apex_json.write( 'attribute_10' , p_dynamic_action_render_result.attribute_10 ); 27 | apex_json.write( 'attribute_11' , p_dynamic_action_render_result.attribute_11 ); 28 | apex_json.write( 'attribute_12' , p_dynamic_action_render_result.attribute_12 ); 29 | apex_json.write( 'attribute_13' , p_dynamic_action_render_result.attribute_13 ); 30 | apex_json.write( 'attribute_14' , p_dynamic_action_render_result.attribute_14 ); 31 | apex_json.write( 'attribute_15' , p_dynamic_action_render_result.attribute_15 ); 32 | 33 | apex_json.close_object; 34 | 35 | return apex_json.get_clob_output; 36 | 37 | end printAttributes; 38 | 39 | 40 | ------------------------ 41 | function printAttributes( 42 | p_plugin in apex_plugin.t_plugin 43 | ) return clob is 44 | 45 | begin 46 | 47 | apex_json.initialize_clob_output; 48 | 49 | apex_json.open_object; 50 | apex_json.write( 'type', 'apex_plugin.t_plugin' ); 51 | 52 | apex_json.write( 'name' , p_plugin.name ); 53 | apex_json.write( 'file_prefix' , p_plugin.file_prefix ); 54 | apex_json.write( 'attribute_01', p_plugin.attribute_01 ); 55 | apex_json.write( 'attribute_02', p_plugin.attribute_02 ); 56 | apex_json.write( 'attribute_03', p_plugin.attribute_03 ); 57 | apex_json.write( 'attribute_04', p_plugin.attribute_04 ); 58 | apex_json.write( 'attribute_05', p_plugin.attribute_05 ); 59 | apex_json.write( 'attribute_06', p_plugin.attribute_06 ); 60 | apex_json.write( 'attribute_07', p_plugin.attribute_07 ); 61 | apex_json.write( 'attribute_08', p_plugin.attribute_08 ); 62 | apex_json.write( 'attribute_09', p_plugin.attribute_09 ); 63 | apex_json.write( 'attribute_10', p_plugin.attribute_10 ); 64 | apex_json.write( 'attribute_11', p_plugin.attribute_11 ); 65 | apex_json.write( 'attribute_12', p_plugin.attribute_12 ); 66 | apex_json.write( 'attribute_13', p_plugin.attribute_13 ); 67 | apex_json.write( 'attribute_14', p_plugin.attribute_14 ); 68 | apex_json.write( 'attribute_15', p_plugin.attribute_15 ); 69 | 70 | apex_json.close_object; 71 | 72 | return apex_json.get_clob_output; 73 | 74 | end printAttributes; 75 | 76 | ------------------------ 77 | function printAttributes( 78 | p_dynamic_action in apex_plugin.t_dynamic_action 79 | ) return clob is 80 | 81 | begin 82 | 83 | apex_json.initialize_clob_output; 84 | 85 | apex_json.open_object; 86 | apex_json.write( 'type', 'apex_plugin.t_dynamic_action' ); 87 | 88 | apex_json.write( 'id' , p_dynamic_action.id , false ); 89 | apex_json.write( 'action' , p_dynamic_action.action , false ); 90 | apex_json.write( 'attribute_01', p_dynamic_action.attribute_01, true ); 91 | apex_json.write( 'attribute_02', p_dynamic_action.attribute_02, true ); 92 | apex_json.write( 'attribute_03', p_dynamic_action.attribute_03, true ); 93 | apex_json.write( 'attribute_04', p_dynamic_action.attribute_04, true ); 94 | apex_json.write( 'attribute_05', p_dynamic_action.attribute_05, true ); 95 | apex_json.write( 'attribute_06', p_dynamic_action.attribute_06, true ); 96 | apex_json.write( 'attribute_07', p_dynamic_action.attribute_07, true ); 97 | apex_json.write( 'attribute_08', p_dynamic_action.attribute_08, true ); 98 | apex_json.write( 'attribute_09', p_dynamic_action.attribute_09, true ); 99 | apex_json.write( 'attribute_10', p_dynamic_action.attribute_10, true ); 100 | apex_json.write( 'attribute_11', p_dynamic_action.attribute_11, true ); 101 | apex_json.write( 'attribute_12', p_dynamic_action.attribute_12, true ); 102 | apex_json.write( 'attribute_13', p_dynamic_action.attribute_13, true ); 103 | apex_json.write( 'attribute_14', p_dynamic_action.attribute_14, true ); 104 | apex_json.write( 'attribute_15', p_dynamic_action.attribute_15, true ); 105 | 106 | apex_json.close_object; 107 | 108 | return apex_json.get_clob_output; 109 | 110 | end printAttributes; 111 | 112 | -------------------------------- 113 | function getColumnNamesFromQuery( 114 | p_string in varchar2 115 | ) return clob is 116 | v_count number; 117 | v_pattern varchar2(50) := '#.+?#'; 118 | 119 | begin 120 | apex_json.initialize_clob_output; 121 | 122 | v_count := regexp_count(p_string, v_pattern, 1, 'm'); 123 | 124 | apex_json.open_object; 125 | apex_json.open_array('queryColumns'); 126 | 127 | for i in 1..v_count loop 128 | apex_json.write( trim(both '#' from regexp_substr(p_string, v_pattern, 1, i, 'm') ) ); 129 | end loop; 130 | 131 | apex_json.close_array; 132 | apex_json.close_object; 133 | 134 | return apex_json.get_clob_output; 135 | end; 136 | 137 | ------------------------- 138 | function getBindVariables( 139 | p_string in varchar2 140 | ) return clob is 141 | l_names DBMS_SQL.VARCHAR2_TABLE; 142 | begin 143 | l_names := WWV_FLOW_UTILITIES.GET_BINDS( p_string ); 144 | 145 | apex_json.initialize_clob_output; 146 | 147 | apex_json.open_object; 148 | apex_json.open_array('queryItems'); 149 | 150 | for i in 1..l_names.count loop 151 | apex_json.write( trim(both ':' from l_names(i) ) ); 152 | end loop; 153 | 154 | apex_json.close_array; 155 | apex_json.close_object; 156 | 157 | return apex_json.get_clob_output; 158 | 159 | end getBindVariables; 160 | 161 | ------------------------------- 162 | function getPluginAppAttributes( 163 | p_plugin in apex_plugin.t_plugin 164 | ) return varchar2 is 165 | attr_app_expand_time number := NVL(p_plugin.attribute_01, 200); 166 | attr_app_collapse_time number := NVL(p_plugin.attribute_02, 400); 167 | begin 168 | apex_json.initialize_clob_output; 169 | 170 | apex_json.open_object; 171 | apex_json.open_object('plugin'); 172 | apex_json.write('animationTime', attr_app_expand_time ); 173 | apex_json.write('closeOtherDuration', attr_app_collapse_time ); 174 | apex_json.close_object; 175 | apex_json.close_object; 176 | 177 | return apex_json.get_clob_output; 178 | 179 | end getPluginAppAttributes; 180 | 181 | ---------------------------- 182 | function pretius_row_details ( 183 | p_dynamic_action in apex_plugin.t_dynamic_action, 184 | p_plugin in apex_plugin.t_plugin 185 | ) return apex_plugin.t_dynamic_action_render_result 186 | is 187 | l_result apex_plugin.t_dynamic_action_render_result; 188 | 189 | l_attr_nestedQuery varchar2(32767) := p_dynamic_action.attribute_01; 190 | l_attr_dc_settings varchar2(100) := p_dynamic_action.attribute_02; 191 | 192 | l_attr_mode varchar2(100) := p_dynamic_action.attribute_03; 193 | l_attr_customTemplate varchar2(32767) := p_dynamic_action.attribute_04; 194 | l_attr_customCallback varchar2(32767) := p_dynamic_action.attribute_05; 195 | l_attr_bgColor varchar2(20) := NVL( p_dynamic_action.attribute_06, '#EBEBEB' ); 196 | l_attr_setMaxHeight number := p_dynamic_action.attribute_07; 197 | l_attr_borderColor varchar2(20) := NVL( p_dynamic_action.attribute_08, '#c5c5c5' ); 198 | l_attr_highlightColor varchar2(20) := NVL( p_dynamic_action.attribute_09, '#F2F2F2' ); 199 | l_attr_cc_settings varchar2(100) := p_dynamic_action.attribute_10; 200 | l_attr_noDataFound varchar2(32767) := p_dynamic_action.attribute_11; 201 | l_attr_spinnerOptions varchar2(100) := NVL( p_dynamic_action.attribute_12, 'ATR' ); 202 | l_attr_defaultTemplate varchar2(4000) := NVL(p_dynamic_action.attribute_13, '#DEFAULT_TEMPLATE#'); 203 | l_attr_dt_settings varchar2(100) := p_dynamic_action.attribute_14; 204 | /* 205 | p_dynamic_action.attribute_12; 206 | p_dynamic_action.attribute_13; 207 | p_dynamic_action.attribute_14; 208 | p_dynamic_action.attribute_15; 209 | */ 210 | attr_app_embedMustache boolean := CASE WHEN p_plugin.attribute_03 = 'Y' then true else false end; 211 | 212 | begin 213 | l_result.ajax_identifier := wwv_flow_plugin.get_ajax_identifier; 214 | l_result.javascript_function := ' 215 | function(){ 216 | pretiusNestedReport(this, '||getColumnNamesFromQuery( l_attr_nestedQuery )||', '||getBindVariables( l_attr_nestedQuery )||', true, '||getPluginAppAttributes( p_plugin )||'); 217 | } 218 | '; 219 | --l_result.attribute_01 := p_dynamic_action.attribute_01; --tajne, bo to zapytaie SQL, ktore mogloby byc dostepne przez this.options 220 | l_result.attribute_02 := l_attr_dc_settings; 221 | l_result.attribute_03 := l_attr_mode; 222 | l_result.attribute_04 := l_attr_customTemplate; 223 | l_result.attribute_05 := l_attr_customCallback; 224 | l_result.attribute_06 := l_attr_bgColor; 225 | l_result.attribute_07 := l_attr_setMaxHeight; 226 | l_result.attribute_08 := l_attr_borderColor; 227 | l_result.attribute_09 := l_attr_highlightColor; 228 | l_result.attribute_10 := l_attr_cc_settings; 229 | l_result.attribute_11 := l_attr_noDataFound; 230 | l_result.attribute_12 := l_attr_spinnerOptions; 231 | l_result.attribute_13 := l_attr_defaultTemplate; 232 | l_result.attribute_14 := l_attr_dt_settings; 233 | --l_result.attribute_15 := p_dynamic_action.attribute_15; 234 | 235 | --add mustache library 236 | if attr_app_embedMustache then 237 | 238 | apex_javascript.add_library( 239 | p_name => 'mustache', 240 | p_directory => p_plugin.file_prefix, 241 | p_version => null 242 | ); 243 | 244 | end if; 245 | 246 | if apex_application.G_DEBUG then 247 | 248 | APEX_PLUGIN_UTIL.DEBUG_DYNAMIC_ACTION ( 249 | p_plugin => p_plugin, 250 | p_dynamic_action => p_dynamic_action 251 | ); 252 | 253 | apex_javascript.add_onload_code (' 254 | apex.debug.info("p_dynamic_action", '||printAttributes( p_dynamic_action )||'); 255 | apex.debug.info("p_plugin", '||printAttributes( p_plugin )||'); 256 | apex.debug.info("l_result", '||printAttributes( l_result )||'); 257 | '); 258 | 259 | end if; 260 | 261 | return l_result; 262 | 263 | end pretius_row_details; 264 | 265 | -------------------- 266 | function clean_query( 267 | p_query in varchar2 268 | ) return varchar2 is 269 | l_query varchar2(32767) := p_query; 270 | begin 271 | loop 272 | if substr(l_query,-1) in (chr(10),chr(13),';',' ','/') then 273 | l_query := substr(l_query,1,length(l_query)-1); 274 | else 275 | exit; 276 | end if; 277 | end loop; 278 | 279 | return l_query; 280 | 281 | end clean_query; 282 | 283 | ----------------------- 284 | function is_valid_query( 285 | p_query in varchar2 286 | ) return varchar2 is 287 | l_source_query varchar2(32767) := p_query; 288 | l_source_queryv varchar2(32767); 289 | l_report_cursor integer; 290 | begin 291 | if l_source_query is not null then 292 | if 293 | substr(upper(ltrim(l_source_query)),1,6) != 'SELECT' 294 | and substr(upper(ltrim(l_source_query)),1,4) != 'WITH' 295 | then 296 | return 'Query must begin with SELECT or WITH'; 297 | end if; 298 | 299 | l_source_query := clean_query( l_source_query ); 300 | l_source_queryv := sys.dbms_assert.noop( str => l_source_query ); 301 | 302 | begin 303 | l_report_cursor := sys.dbms_sql.open_cursor; 304 | sys.dbms_sql.parse( l_report_cursor, l_source_queryv, SYS.DBMS_SQL.NATIVE ); 305 | sys.dbms_sql.close_cursor(l_report_cursor); 306 | exception 307 | when others then 308 | if sys.dbms_sql.is_open( l_report_cursor ) then 309 | sys.dbms_sql.close_cursor( l_report_cursor ); 310 | end if; 311 | return sqlerrm;--||': '||chr(10)||chr(10)||l_source_query; 312 | end; 313 | end if; 314 | 315 | return null; 316 | exception 317 | when others then 318 | return SQLERRM;--||':'||chr(10)||chr(10)||p_query; 319 | end is_valid_query; 320 | 321 | ---------------------------- 322 | function getColumnTypeString( 323 | p_col_type in number 324 | ) return varchar2 is 325 | l_col_type varchar2(50); 326 | begin 327 | if p_col_type = 1 then 328 | l_col_type := 'VARCHAR2'; 329 | 330 | elsif p_col_type = 2 then 331 | l_col_type := 'NUMBER'; 332 | 333 | elsif p_col_type = 12 then 334 | l_col_type := 'DATE'; 335 | 336 | elsif p_col_type in (180,181,231) then 337 | l_col_type := 'TIMESTAMP'; 338 | 339 | if p_col_type = 231 then 340 | l_col_type := 'TIMESTAMP_LTZ'; 341 | end if; 342 | 343 | elsif p_col_type = 112 then 344 | l_col_type := 'CLOB'; 345 | 346 | elsif p_col_type = 113 then 347 | 348 | l_col_type := 'BLOB'; 349 | 350 | elsif p_col_type = 96 then 351 | l_col_type := 'CHAR'; 352 | 353 | else 354 | l_col_type := 'OTHER'; 355 | end if; 356 | 357 | return l_col_type; 358 | 359 | end getColumnTypeString; 360 | 361 | --------------------------------- 362 | function pretius_row_details_ajax( 363 | p_dynamic_action in apex_plugin.t_dynamic_action, 364 | p_plugin in apex_plugin.t_plugin 365 | ) return apex_plugin.t_dynamic_action_ajax_result 366 | is 367 | l_status number; 368 | l_desc_col_no number := 0; 369 | 370 | l_ajax_column_name varchar2(4000); 371 | l_ajax_column_values varchar2(4000); 372 | 373 | l_sql varchar2(32767); 374 | l_delimeter varchar2(1) := ':'; 375 | l_parseResult varchar2(4000); 376 | 377 | l_result apex_plugin.t_dynamic_action_ajax_result; 378 | 379 | l_columnNames apex_application_global.vc_arr2; 380 | l_columnValues apex_application_global.vc_arr2; 381 | 382 | l_sys_cursor sys_refcursor; 383 | 384 | l_cursor pls_integer; 385 | 386 | l_desc_col_info sys.dbms_sql.desc_tab2; 387 | 388 | l_apex_items_names DBMS_SQL.VARCHAR2_TABLE; 389 | begin 390 | 391 | l_ajax_column_name := apex_application.g_x01; 392 | l_ajax_column_values := apex_application.g_x02; 393 | 394 | l_sql := p_dynamic_action.attribute_01; 395 | l_apex_items_names := WWV_FLOW_UTILITIES.GET_BINDS( l_sql ); 396 | 397 | l_columnNames := apex_util.string_to_table( l_ajax_column_name , l_delimeter ); 398 | l_columnValues := apex_util.string_to_table( l_ajax_column_values, l_delimeter ); 399 | 400 | if l_columnNames.count <> l_columnValues.count then 401 | apex_json.open_object; 402 | apex_json.write('addInfo', 'The number of column names must be equal to the number of column values.
Check whether the query columns exist in parent report.'); 403 | apex_json.write('error', 'Column names = "'||l_ajax_column_name||'"'||chr(10)||'Column values = "'||l_ajax_column_values||'"'); 404 | apex_json.close_object; 405 | return null; 406 | end if; 407 | 408 | --replacing space within column name is required to work with column aliases 409 | for i in 1..l_columnNames.count loop 410 | l_sql := replace( l_sql, chr(39)||'#'||l_columnNames(i)||'#'||chr(39) , ':' || replace(l_columnNames(i), ' ', '') ); 411 | l_sql := replace( l_sql, '#'||l_columnNames(i)||'#' , ':' || replace(l_columnNames(i), ' ', '') ); 412 | end loop; 413 | 414 | l_parseResult := is_valid_query( l_sql ); 415 | 416 | if l_parseResult is not null then 417 | apex_json.open_object; 418 | apex_json.write('addInfo', 'Nested report SQL query is not valid'); 419 | apex_json.write('error', l_parseResult); 420 | --apex_json.write('query', l_sql); 421 | apex_json.close_object; 422 | return null; 423 | end if; 424 | 425 | -- open l_cursor; 426 | l_cursor := dbms_sql.open_cursor; 427 | dbms_sql.parse (l_cursor, l_sql, dbms_sql.native); 428 | 429 | -- bind items 430 | begin 431 | 432 | for i in 1..l_apex_items_names.count loop 433 | dbms_sql.bind_variable (l_cursor, l_apex_items_names(i), v( trim(both ':' from l_apex_items_names(i)) ) ); 434 | end loop; 435 | 436 | exception 437 | when others then 438 | apex_json.open_object; 439 | apex_json.write('addInfo', 'While binding APEX items error occured'); 440 | apex_json.write('error', SQLERRM); 441 | apex_json.close_object; 442 | return null; 443 | end; 444 | 445 | --bind all the values 446 | --replacing space within column name is required to work with column aliases 447 | begin 448 | for i in 1 .. l_columnNames.count loop 449 | dbms_sql.bind_variable (l_cursor, replace(l_columnNames(i), ' ', ''), l_columnValues(i)); 450 | end loop; 451 | exception 452 | when others then 453 | apex_json.open_object; 454 | apex_json.write('addInfo', 'While binding query variables error occured'); 455 | apex_json.write('error', SQLERRM); 456 | apex_json.close_object; 457 | return null; 458 | end; 459 | 460 | -- describe columns 461 | sys.dbms_sql.describe_columns2( l_cursor, l_desc_col_no , l_desc_col_info); 462 | 463 | begin 464 | l_status := dbms_sql.execute(l_cursor); 465 | exception 466 | when others then 467 | apex_json.open_object; 468 | apex_json.write('addInfo', 'While executing query error occured '); 469 | apex_json.write('error', SQLERRM); 470 | apex_json.close_object; 471 | return null; 472 | end; 473 | 474 | l_sys_cursor := dbms_sql.to_refcursor(l_cursor); 475 | 476 | --apex_json.initialize_clob_output; 477 | 478 | apex_json.open_object; 479 | apex_json.write( 'data', l_sys_cursor ); 480 | apex_json.open_array('headers'); 481 | 482 | for i in 1..l_desc_col_no loop 483 | apex_json.open_object; 484 | apex_json.write('COLUMN_NAME', l_desc_col_info(i).col_name); 485 | apex_json.write('COLUMN_TYPE', getColumnTypeString( l_desc_col_info(i).col_type ) ); 486 | apex_json.close_object; 487 | end loop; 488 | 489 | apex_json.close_array; 490 | 491 | apex_json.write( 'x01', l_ajax_column_name, true ); 492 | apex_json.write( 'x02', l_ajax_column_values, true ); 493 | 494 | apex_json.close_object; 495 | 496 | --htp.p( apex_json.get_clob_output ); 497 | 498 | return l_result; 499 | exception 500 | when others then 501 | apex_json.open_object; 502 | apex_json.write('addInfo', 'Unknown ajax error'); 503 | apex_json.write('error', SQLERRM); 504 | apex_json.close_object; 505 | htp.p( apex_json.get_clob_output ); 506 | return l_result; 507 | end pretius_row_details_ajax; 508 | 509 | end "PRETIUS_APEX_NESTED_REPORTS"; 510 | -------------------------------------------------------------------------------- /src/PRETIUS_APEX_NESTED_REPORTS.sql: -------------------------------------------------------------------------------- 1 | create or replace package PRETIUS_APEX_NESTED_REPORTS as 2 | 3 | function pretius_row_details ( 4 | p_dynamic_action in apex_plugin.t_dynamic_action, 5 | p_plugin in apex_plugin.t_plugin 6 | ) return apex_plugin.t_dynamic_action_render_result; 7 | 8 | function pretius_row_details_ajax( 9 | p_dynamic_action in apex_plugin.t_dynamic_action, 10 | p_plugin in apex_plugin.t_plugin 11 | ) return apex_plugin.t_dynamic_action_ajax_result; 12 | 13 | end; 14 | --------------------------------------------------------------------------------