From 8d7c6bb13c821dc48c9eb09e8eb006565c09a5b4 Mon Sep 17 00:00:00 2001 From: Daniel Cothran <dcothran@jsi.local> Date: Thu, 9 Mar 2017 11:37:25 -0500 Subject: [PATCH] Initial commit of the 8.x-1.x version of Charts --- README.txt | 128 +++-- charts.api.php | 24 +- charts.info | 9 - charts.info.yml | 6 + charts.install | 23 +- charts.libraries.yml | 9 + charts.module | 472 +++------------- charts.permissions.yml | 6 + charts.routing.yml | 7 + charts.services.yml | 6 + charts.settings.yml | 0 charts.theme.inc | 38 ++ includes/charts.examples.inc | 274 --------- includes/charts.pages.inc | 406 ++++++++++---- js/charts.admin.js | 188 +++---- modules/charts_c3/charts_c3.info.yml | 7 + modules/charts_c3/charts_c3.libraries.yml | 35 ++ modules/charts_c3/charts_c3.module | 80 +++ modules/charts_c3/composer.json | 56 ++ modules/charts_c3/js/charts_c3.js | 19 + .../charts_c3/src/Settings/CThree/CThree.php | 84 +++ .../src/Settings/CThree/ChartAxis.php | 32 ++ .../src/Settings/CThree/ChartColor.php | 31 ++ .../src/Settings/CThree/ChartData.php | 76 +++ .../src/Settings/CThree/ChartDimensions.php | 31 ++ .../src/Settings/CThree/ChartLabel.php | 31 ++ .../src/Settings/CThree/ChartTitle.php | 31 ++ .../src/Settings/CThree/ChartType.php | 31 ++ modules/charts_google/charts_google.inc | 18 +- modules/charts_google/charts_google.info | 5 - modules/charts_google/charts_google.info.yml | 7 + modules/charts_google/charts_google.js | 107 ---- .../charts_google/charts_google.libraries.yml | 18 + modules/charts_google/charts_google.module | 96 +++- modules/charts_google/composer.json | 35 ++ modules/charts_google/js/charts_google.js | 54 ++ .../src/Settings/Google/ChartArea.php | 31 ++ .../src/Settings/Google/ChartType.php | 32 ++ .../src/Settings/Google/GoogleOptions.php | 91 +++ .../src/Settings/Google/HorizontalAxis.php | 37 ++ .../src/Settings/Google/VerticalAxis.php | 7 + .../charts_highcharts/charts_highcharts.inc | 61 ++- .../charts_highcharts/charts_highcharts.info | 6 - .../charts_highcharts.info.yml | 7 + .../charts_highcharts.install | 12 +- .../charts_highcharts/charts_highcharts.js | 17 - .../charts_highcharts.libraries.yml | 20 + .../charts_highcharts.module | 109 ++-- modules/charts_highcharts/composer.json | 35 ++ .../charts_highcharts/js/charts_highcharts.js | 19 + .../src/Settings/Highcharts/ChartCredits.php | 31 ++ .../src/Settings/Highcharts/ChartLabel.php | 31 ++ .../src/Settings/Highcharts/ChartLegend.php | 151 +++++ .../src/Settings/Highcharts/ChartTitle.php | 31 ++ .../src/Settings/Highcharts/ChartType.php | 31 ++ .../Settings/Highcharts/DataLabelStatus.php | 31 ++ .../src/Settings/Highcharts/DataLabels.php | 31 ++ .../src/Settings/Highcharts/Highcharts.php | 152 +++++ .../src/Settings/Highcharts/Label.php | 31 ++ .../src/Settings/Highcharts/PlotOptions.php | 31 ++ .../src/Settings/Highcharts/Tooltip.php | 31 ++ .../src/Settings/Highcharts/Xaxis.php | 61 +++ .../src/Settings/Highcharts/Yaxis.php | 46 ++ .../src/Settings/Highcharts/YaxisLabel.php | 31 ++ .../src/Settings/Highcharts/YaxisTitle.php | 32 ++ src/Form/ChartsConfigForm.php | 518 ++++++++++++++++++ .../display/ChartsPluginDisplayChart.php | 168 ++++++ .../views/style/ChartsPluginStyleChart.php | 251 ++++----- src/Services/ChartAttachmentService.php | 30 + .../ChartAttachmentServiceInterface.php | 18 + src/Services/ChartService.php | 36 ++ src/Services/ChartServiceInterface.php | 23 + src/Util/Util.php | 77 +++ templates/views-view-charts.html.twig | 15 + views/charts.views.inc | 44 -- views/charts.views_default.inc | 163 ------ views/charts_plugin_display_chart.inc | 130 ----- 77 files changed, 3530 insertions(+), 1659 deletions(-) delete mode 100644 charts.info create mode 100644 charts.info.yml create mode 100644 charts.libraries.yml create mode 100644 charts.permissions.yml create mode 100644 charts.routing.yml create mode 100644 charts.services.yml create mode 100644 charts.settings.yml create mode 100644 charts.theme.inc delete mode 100644 includes/charts.examples.inc create mode 100644 modules/charts_c3/charts_c3.info.yml create mode 100644 modules/charts_c3/charts_c3.libraries.yml create mode 100644 modules/charts_c3/charts_c3.module create mode 100644 modules/charts_c3/composer.json create mode 100644 modules/charts_c3/js/charts_c3.js create mode 100644 modules/charts_c3/src/Settings/CThree/CThree.php create mode 100644 modules/charts_c3/src/Settings/CThree/ChartAxis.php create mode 100644 modules/charts_c3/src/Settings/CThree/ChartColor.php create mode 100644 modules/charts_c3/src/Settings/CThree/ChartData.php create mode 100644 modules/charts_c3/src/Settings/CThree/ChartDimensions.php create mode 100644 modules/charts_c3/src/Settings/CThree/ChartLabel.php create mode 100644 modules/charts_c3/src/Settings/CThree/ChartTitle.php create mode 100644 modules/charts_c3/src/Settings/CThree/ChartType.php delete mode 100644 modules/charts_google/charts_google.info create mode 100644 modules/charts_google/charts_google.info.yml delete mode 100644 modules/charts_google/charts_google.js create mode 100644 modules/charts_google/charts_google.libraries.yml create mode 100644 modules/charts_google/composer.json create mode 100644 modules/charts_google/js/charts_google.js create mode 100644 modules/charts_google/src/Settings/Google/ChartArea.php create mode 100644 modules/charts_google/src/Settings/Google/ChartType.php create mode 100644 modules/charts_google/src/Settings/Google/GoogleOptions.php create mode 100644 modules/charts_google/src/Settings/Google/HorizontalAxis.php create mode 100644 modules/charts_google/src/Settings/Google/VerticalAxis.php delete mode 100644 modules/charts_highcharts/charts_highcharts.info create mode 100644 modules/charts_highcharts/charts_highcharts.info.yml delete mode 100644 modules/charts_highcharts/charts_highcharts.js create mode 100644 modules/charts_highcharts/charts_highcharts.libraries.yml create mode 100644 modules/charts_highcharts/composer.json create mode 100644 modules/charts_highcharts/js/charts_highcharts.js create mode 100644 modules/charts_highcharts/src/Settings/Highcharts/ChartCredits.php create mode 100644 modules/charts_highcharts/src/Settings/Highcharts/ChartLabel.php create mode 100644 modules/charts_highcharts/src/Settings/Highcharts/ChartLegend.php create mode 100644 modules/charts_highcharts/src/Settings/Highcharts/ChartTitle.php create mode 100644 modules/charts_highcharts/src/Settings/Highcharts/ChartType.php create mode 100644 modules/charts_highcharts/src/Settings/Highcharts/DataLabelStatus.php create mode 100644 modules/charts_highcharts/src/Settings/Highcharts/DataLabels.php create mode 100644 modules/charts_highcharts/src/Settings/Highcharts/Highcharts.php create mode 100644 modules/charts_highcharts/src/Settings/Highcharts/Label.php create mode 100644 modules/charts_highcharts/src/Settings/Highcharts/PlotOptions.php create mode 100644 modules/charts_highcharts/src/Settings/Highcharts/Tooltip.php create mode 100644 modules/charts_highcharts/src/Settings/Highcharts/Xaxis.php create mode 100644 modules/charts_highcharts/src/Settings/Highcharts/Yaxis.php create mode 100644 modules/charts_highcharts/src/Settings/Highcharts/YaxisLabel.php create mode 100644 modules/charts_highcharts/src/Settings/Highcharts/YaxisTitle.php create mode 100644 src/Form/ChartsConfigForm.php create mode 100644 src/Plugin/views/display/ChartsPluginDisplayChart.php rename views/charts_plugin_style_chart.inc => src/Plugin/views/style/ChartsPluginStyleChart.php (58%) create mode 100644 src/Services/ChartAttachmentService.php create mode 100644 src/Services/ChartAttachmentServiceInterface.php create mode 100644 src/Services/ChartService.php create mode 100644 src/Services/ChartServiceInterface.php create mode 100644 src/Util/Util.php create mode 100644 templates/views-view-charts.html.twig delete mode 100644 views/charts.views.inc delete mode 100644 views/charts.views_default.inc delete mode 100644 views/charts_plugin_display_chart.inc diff --git a/README.txt b/README.txt index e713a70..f70eb5a 100644 --- a/README.txt +++ b/README.txt @@ -1,25 +1,30 @@ Charts ====== -Charts module provides a unified format to build any kind of chart with any +The Charts module provides a unified format to build any kind of chart with any chart provider. -Each chart solution found on internet, like Google Charts and Highcharts, -have a specific data scheme. Its very hard and even impossible to build a unique -chart data that would be used in more that one chart provider. Or users get -binded to a solution forever or they have to rewrite all exported data again. +Each chart solution found on internet, such as Google Charts or Highcharts, +has a specific data scheme. Its very hard and even impossible to build a unique +chart data scheme that would be used in more that one chart provider. Or users +get bound to a solution forever. Or they have to rewrite all exported data +again. -Thats why Charts is so great. It uses a standard data scheme do describe charts -data and thru filters, it converts automatically to each solution. You might +That's why Charts is so great. It uses a standard data scheme to describe charts +data, and through filters, it automatically converts to each solution. You can change to another solution at anytime. -The Chart schema is very similiar to Drupal's Form API schema. +The Chart schema is very similar to Drupal's Form API schema. Chart Providers --------------- -Out of the Box, you will be able to use 2 chart solutions. Which one of them -has particular advantages and disadvantages. +Out of the Box, you will be able to use 3 chart solutions. Each of them has +particular advantages and disadvantages. + +* C3: This library is a D3-based reusable chart library makes it easy to + generate D3-based charts by wrapping the code required to construct the + entire chart. You don't need to write D3 code any more. * Google Charts: This library does not require any external downloads. It generates interactive charts using SVG and VML. @@ -29,19 +34,30 @@ has particular advantages and disadvantages. animations, it requires a commercial license. It's free for non-commercial use. See http://www.highcharts.com -Installing Highcharts +Installing Libraries --------------------- -If you decide to use the Highcharts library, you'll need to first install the -Libraries module for Drupal: https://drupal.org/project/libraries +The 8.x version of Charts is designed to be used with Composer. After you +enable a sub-module (charts_c3, charts_google, or charts_highcharts), you need +to download the library. Here's how I would do it for C3 (starting in my Drupal +root directory: + +cd modules/charts/modules/charts_c3 +composer install + +This will set up the following directory structure within your charts_c3 +directory: -Then you'll need to download Highcharts and place it in sites/all/libraries -directory. The highcharts.js file should be located at: -sites/all/libraries/highcharts/js/highcharts.js. You will also need to remove a -directory called "exporting-server" within the Highcharts download. This -directory contains sample code for exporting charts that may compromise the -security of your site. The module will throw an error if this directory is left -in place. +vendor + - cthree + - css + - c3.min.css + - c3.min.js + - dthree + - d3.v3.min.js + +There are numerous tutorials on Drupal.org and elsewhere on the web if you are +looking for more information about how to use Composer with Drupal 8. Creating Charts in the UI ------------------------- @@ -50,37 +66,29 @@ This module provides a configuration page at admin/config/content/charts. You may set site-wide defaults on this page (for example set the default color scheme). -In order to actuall create a chart through the UI, you'll need to use Views +In order to actually create a chart through the UI, you'll need to use Views module. - Create a new view: Visit admin/structure/views/add and select the display format of "Chart" for your new page or block. -- Enable aggregation on the view: - In order to display data, most of the time you'll need enable aggregation on - the view. Under the right column of settings, there is a section for - "Use aggregation". Enable this setting. - - Add a label field: Under the "Fields" section, add a field you would like to be used as labels - along one axis of the chart (or slices of the pie). In the "Aggregation type" - setting, set it to "Group results together". + along one axis of the chart (or slices of the pie). - Add data fields: - Now a second field which will be used to determine the data values. Usually - this will be an ID field, such as NID or CID. The label you give this field - will be used in the chart's legend to represent this series. After adding the - field, set the "Aggregation type" for this field to "Count". Do this again for + Now a second field that will be used to determine the data values. If you + are visualizing an Event content type, this field might be + field_number_attendees. The label you give this field will be used in the + chart's legend to represent this series. Do this again for each different quantity you would like to chart. Note that some charts (e.g. Pie) only support a single data column. - Configure the chart display: Click on the "Settings" link in the Format section to configure the chart. Select your chart type. Some options may not be available to all chart types - and will be adjusted based on the type selected. Because no live preview is - available in Charts, you may need to open a saved version of the view in a - different window to see the impact of your changes. + and will be adjusted based on the type selected. - Save your view. @@ -91,39 +99,29 @@ the result of the chart will be if it's been laid out in a table first. Creating Multiple Series and Combo Charts in the UI --------------------------------------------------- -When using Views to build your charts, you may find it difficult to retrieve -more than a single set of data generated by a COUNT() query. For example if you -wanted to retrieve the age of all your site users, but display "Male" and -"Female" values in a column chart at the same time, constructing the underlying -table of data is quite difficult. - -To solve this problem, you can combine multiple charts on top of each other. The -"parent" chart provides the global information, such as the height, width, -title, and other properties. Charts that are "children" provide only data and -(optionally) a secondary axis. After you've assembled the first series of data -in your chart according to the instructions in the "Creating Charts in the UI" -section, add a new display to the same view of the type "Chart Add-on". The -"Chart Add-on" type is added the same way you would add a new Page or Block -display, from the "+ Add" menu at the top of the view configuration. - -After this new display has been added, find the setting for "Combine with parent -chart" and change this value to point at the parent chart you have already -assembled. Then adjust the settings for the child chart to pull in different -data (often by overriding the filter settings). Now you can go back to your -parent display, and see that the results from the child chart have been -merged into the results from the parent chart. You can even use this approach -to combine different types of charts, such as a line chart over the top of -a column chart. Note that not all chart types can be combined together and -invalid combinations may cause your chart to throw errors. - -Create Charts through the API ------------------------------ - -Charts module includes an extensive API for creating charts through code. See -the charts.api.php file included with this module for documentation. +A major difference between the Drupal 7 and Drupal 8 versions of this module is +that the Drupal 8 module uses a Chart Attachment plugin for creating a separate +chart series that can be attached to a parent display. + +As of the -alpha release, there is a lot of work needed here. The following are +identified questions/issues, which we would like your help with: + +1) Is a Chart Attachment (previously Chart Add-on) display still needed with +the improvements made to the module in Drupal 8? +2) Todo: determine how to respect the "Create Secondary Axis" option on the +Chart Attachment +3) Todo: determine how to respect the selection of a different chart type (for +example, the display could be a bar graph and the attachment could be a line +graph) Support ------- For bug reports and feature requests please use the Drupal.org issue tracker: http://drupal.org/project/issues/charts. + +We welcome your support in improving code documentation, tests, and providing +example use-cases not addressed by the existing module. + +If you are interested in creating your own sub-module for a library not +currently supported (for example, Flot), please contact @andileco diff --git a/charts.api.php b/charts.api.php index 83f52f9..d06d5d5 100644 --- a/charts.api.php +++ b/charts.api.php @@ -1,4 +1,5 @@ <?php + /** * @file * Documentation on hooks provided by the Charts module. @@ -47,14 +48,10 @@ * chart_data, chart_xaxis, and chart_yaxis). For a full list, see the * charts_element_info() function. * - * This module also includes a number of examples for reference, which can be - * views at "charts/examples". The source code may be read in the - * "charts.examples.inc" file in the includes directory. * * @see charts_element_info() - */ - -/** + * + * * Alter an individual chart before it is printed. * * @param $chart @@ -77,6 +74,7 @@ function hook_chart_alter(&$chart, $chart_id) { * name instead of being passed in as an argument. * * @see hook_chart_alter() + * @param $chart */ function hook_chart_CHART_ID_alter(&$chart) { } @@ -100,14 +98,16 @@ function hook_chart_CHART_ID_alter(&$chart) { * The chart renderable. This may be used for reference (or read to add * support for new properties), but any changes to this variable will not * have an effect on output. - * @param $chart_id - * The chart ID, derived from the $chart['#chart_id'] property. Note that not + * @internal param $chart_id The chart ID, derived from the $chart['#chart_id'] property. Note that not* The chart ID, derived from the $chart['#chart_id'] property. Note that not * all charts may have a $chart_id. */ -function hook_chart_definition_alter(&$definition, $chart, $chart_id) { +function hook_chart_definition_alter(&$definition, $chart) { if ($chart['#chart_library'] === 'google') { $definition['options']['titleTextStyle']['fontSize'] = 20; } + if ($chart['#chart_library'] === 'c3') { + $definition['options']['titleTextStyle']['fontSize'] = 20; + } elseif ($chart['#chart_library'] === 'highcharts') { $definition['title']['style']['fontSize'] = 20; } @@ -118,8 +118,8 @@ function hook_chart_definition_alter(&$definition, $chart, $chart_id) { * * Same as hook_chart_definition_alter(), only including the $chart_id in the * function name instead of being passed in as an argument. - * @see hook_chart_definition_alter() + * @param $chart */ function hook_chart_definition_CHART_ID_alter(&$chart) { } @@ -140,7 +140,7 @@ function hook_charts_info() { // Specify the chart types your library is capable of providing. 'types' => array('area', 'bar', 'column', 'line', 'pie', 'scatter'), // If your callback function is in a separate file, specify it's location. - 'file' => 'includes/my_charting_library.inc', + // 'file' => 'includes/my_charting_library.inc', ); return $info; } @@ -150,6 +150,7 @@ function hook_charts_info() { * * If your module needs to modify the capabilities of a charting library, such * as to add support for a new chart type, it may do so with this hook. + * @param $info */ function hook_charts_info_alter(&$info) { // Say the Google charts library supports geo charts. @@ -186,6 +187,7 @@ function hook_charts_type_info() { * * If your module needs to modify the capabilities or labels of a paricular * chart type, it may alter the definitions provided by other modules. + * @param $chart_types */ function hook_charts_type_info_alter(&$chart_types) { $chart_types['bar']['stacking'] = FALSE; diff --git a/charts.info b/charts.info deleted file mode 100644 index da7bce6..0000000 --- a/charts.info +++ /dev/null @@ -1,9 +0,0 @@ -name = Charts -description = A charting API for Drupal that provides chart elements and integration with Views. -package = Charts -core = 7.x -php = 5.2 -configure = admin/config/content/charts - -files[] = views/charts_plugin_style_chart.inc -files[] = views/charts_plugin_display_chart.inc diff --git a/charts.info.yml b/charts.info.yml new file mode 100644 index 0000000..30725d4 --- /dev/null +++ b/charts.info.yml @@ -0,0 +1,6 @@ +name: Charts +type: module +description: 'A charting API for Drupal that provides chart elements and integration with Views.' +package: Charts +core: 8.x +configure: charts.settings.form diff --git a/charts.install b/charts.install index fe824aa..dceeb34 100644 --- a/charts.install +++ b/charts.install @@ -9,20 +9,15 @@ */ function charts_requirements($phase) { $requirements = array(); - $t = get_t(); - if ($phase == 'runtime' && !charts_info()) { - $requirements['charts'] = array( - 'title' => $t('Charts'), - 'value' => $t('No Charts provider installed'), - 'severity' => REQUIREMENT_ERROR, - 'description' => $t('Charts core module only provides a a common set of functions. You must install a Charts provider module to create charts.'), - ); - } + // if ($phase == 'runtime' && !charts_info()) { + /* $requirements['charts'] = array( + 'title' => t('Charts'), + 'value' => t('No Charts provider installed'), + 'severity' => REQUIREMENT_ERROR, + 'description' => t('Charts core module only provides a a common set of functions. You must install a Charts provider module to create charts.'), + );*/ + + // } return $requirements; } -/** - * Implements hook_uninstall(). - */ -function charts_uninstall() { -} diff --git a/charts.libraries.yml b/charts.libraries.yml new file mode 100644 index 0000000..e50681f --- /dev/null +++ b/charts.libraries.yml @@ -0,0 +1,9 @@ +charts: + version: 1.x + js: + js/charts.admin.js: {} + dependencies: + - core/jquery + - core/jquery.once + - core/drupal + - core/drupalSettings diff --git a/charts.module b/charts.module index 8e1279d..b96b8ed 100644 --- a/charts.module +++ b/charts.module @@ -1,429 +1,107 @@ <?php -/** - * @file - * Provides elements for rendering charts and Views integration. - */ - -/** - * Constant used in hook_charts_type_info() to declare chart types with a single - * axis. For example a pie chart only has a single dimension. - */ -define('CHARTS_SINGLE_AXIS', 'y_only'); - -/** - * Constant used in hook_charts_type_info() to declare chart types with a dual - * axes. Most charts use this type of data, meaning multiple categories each - * have multiple values. This type of data is usually represented as a table. - */ -define('CHARTS_DUAL_AXIS', 'xy'); - -/** - * Implements hook_menu(). - */ -function charts_menu() { - $items['admin/config/content/charts'] = array( - 'title' => 'Default chart configuration', - 'description' => 'Set the default library, behavior, and style of charts.', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('charts_default_settings_form'), - 'access arguments' => array('administer charts'), - 'file' => 'includes/charts.pages.inc', - ); - $items['charts/examples'] = array( - 'title' => 'Charts examples', - 'page callback' => 'charts_examples', - 'access arguments' => array('access example charts'), - 'file' => 'includes/charts.examples.inc', - 'type' => MENU_NORMAL_ITEM, - ); - $items['charts/examples/built-in'] = array( - 'title' => 'Built-in examples', - 'type' => MENU_DEFAULT_LOCAL_TASK, - ); - return $items; -} - -/** - * Implements hook_element_info(). - */ -function charts_element_info() { - // Create the elements representing charts themselves. - $info['chart'] = array( - '#chart_type' => NULL, // Options: pie, bar, column, etc. - '#chart_id' => NULL, // Machine name to alter this chart individually. - '#title' => NULL, - '#title_color' => '#000', - '#title_font_weight' => 'normal', // Options: normal, bold - '#title_font_style' => 'normal', // Options: normal, italic - '#title_font_size' => 14, // Font size in pixels, e.g. 12. - '#title_position' => 'out', // Options: in, out. - '#colors' => charts_default_colors(), - '#font' => 'Arial', - '#font_size' => 12, // Font size in pixels, e.g. 12. - '#background' => 'transparent', - '#stacking' => NULL, // Bar chart only. Set to TRUE to stack. - '#pre_render' => array('charts_pre_render_element'), - '#tooltips' => TRUE, - '#tooltips_use_html' => FALSE, - '#data_labels' => FALSE, - '#legend' => TRUE, - '#legend_title' => NULL, - '#legend_title_font_weight' => 'bold', // Options: normal, bold - '#legend_title_font_style' => 'normal', // Options: normal, italic - '#legend_title_font_size' => NULL, // CSS value for font size, e.g. 1em or 12px. - '#legend_position' => 'right', // Options: top, right, bottom, left. - '#legend_font_weight' => 'normal', // Options: normal, bold - '#legend_font_style' => 'normal', // Options: normal, italic - '#legend_font_size' => NULL, // CSS value for font size, e.g. 1em or 12px. - '#width' => NULL, - '#height' => NULL, - - '#attributes' => array(), // Applied to the wrapper, not the chart. - '#chart_library' => NULL, // If requiring a particular charting module. - '#raw_options' => array(), // Raw library-specific options passed directly. - ); - - // Properties for x and y axes. - $axis_properties = array( - '#axis_type' => 'linear', // Options: linear, logarithmic, datetime, labels. - '#title' => '', - '#title_color' => '#000', - '#title_font_weight' => 'normal', // Options: normal, bold - '#title_font_style' => 'normal', // Options: normal, italic - '#title_font_size' => 12, // CSS value for font size, e.g. 1em or 12px. - '#labels' => NULL, - '#labels_color' => '#000', - '#labels_font_weight' => 'normal', // Options: normal, bold - '#labels_font_style' => 'normal', // Options: normal, italic - '#labels_font_size' => NULL, // CSS value for font size, e.g. 1em or 12px. - '#labels_rotation' => NULL, // Integer rotation value, e.g. 30, -60 or 90. - '#grid_line_color' => '#ccc', - '#base_line_color' => '#ccc', - '#minor_grid_line_color' => '#e0e0e0', - '#max' => NULL, // Integer max value on this axis. - '#min' => NULL, // Integer minimum value on this axis. - '#opposite' => FALSE, // Display axis on opposite normal side. - ); - $info['chart_xaxis'] = array( - ) + $axis_properties; - $info['chart_yaxis'] = array( - ) + $axis_properties; - - // And then the pieces of charts used within chart types. - $info['chart_data'] = array( - '#title' => NULL, - '#labels' => NULL, - '#data' => array(), - '#color' => NULL, - '#show_in_legend' => TRUE, - '#show_labels' => FALSE, // Show inline labels next to the data. - '#chart_type' => NULL, // If building multicharts. The chart type, e.g. pie. - '#line_width' => 1, // Line chart only. - '#marker_radius' => 3, // Line chart only. Size in pixels, e.g. 1, 5. - '#target_axis' => NULL, // If using multiple axes, key for the matching y axis. - - // Formatting options. - '#decimal_count' => NULL, // The number of digits after the decimal separator. e.g. 2 - '#date_format' => NULL, // A custom date format, e.g. %Y-%m-%d - '#prefix' => NULL, - '#suffix' => NULL, - ); - $info['chart_data_item'] = array( - '#data' => NULL, - '#color' => NULL, - '#title' => NULL, // Often used as content of the tooltip. - ); - - return $info; -} /** - * Implements hook_library(). + * @file + * Charts module file that provides hook_theme. */ -function charts_library() { - $library['charts.admin'] = array( - 'title' => t('Charts admin integration'), - 'version' => '1.0', - 'js' => array( - array('data' => drupal_get_path('module', 'charts') . '/js/charts.admin.js', 'type' => 'file', 'weight' => 10), - ), - ); - return $library; -} +use \Drupal\charts\Util\Util; /** - * Implements hook_permission(). + * {@inheritdoc} */ -function charts_permission() { - return array( - 'administer charts' => array( - 'title' => t('Administer Charts'), - 'description' => t('Visit the <a href="!url">Default chart configuration</a> (used for Charts administration)', array('!url' => url('admin/config/content/charts'))), - ), - 'access example charts' => array( - 'title' => t('Access example charts'), - 'description' => t('Visit the <a href="!url">Charts examples</a> (used for Charts testing and demo)', array('!url' => url('charts/examples'))), - ), - ); -} +function charts_theme($existing, $type, $theme, $path) { -/** - * Implements hook_theme(). - */ -function charts_theme() { return array( - 'charts_settings_fields' => array( - 'render element' => 'element', + 'views_view_charts' => array( + 'variables' => array( + 'view' => NULL, + 'row' => NULL, + ), ), - 'charts_chart' => array( - 'render element' => 'element', - ), - ); -} - -/** - * Implements hook_views_api(). - */ -function charts_views_api() { - return array( - 'api' => 3.0, - 'path' => drupal_get_path('module', 'charts') . '/views', ); } /** - * Main #pre_render callback to expand a chart element. + * {@inheritdoc} */ -function charts_pre_render_element($element) { - $charts_info = charts_info(); - $chart_library = isset($element['#chart_library']) ? $element['#chart_library'] : NULL; +function template_preprocess_views_view_charts(&$variables) { - // Use the first charting library if the requested library is not available. - if (isset($chart_library) && isset($charts_info[$chart_library])) { - $chart_library_info = $charts_info[$chart_library]; - } - else { - $chart_library = key($charts_info); - $chart_library_info = $charts_info[$chart_library]; - } + $options = $variables['view']->style_plugin->options; - if (!isset($chart_library_info)) { - $element['#type'] = 'markup'; - $element['#markup'] = t('No charting library found. Enable a charting module such as Google Charts or Highcharts.'); - return $element; - } + $service = \Drupal::service('charts.charts_attachment'); + $attachmentView = $service->getAttachmentViews(); - // Ensure there's an x and y axis to provide defaults. - $chart_type = chart_get_type($element['#chart_type']); - if ($chart_type['axis'] === CHARTS_DUAL_AXIS) { - foreach (element_children($element) as $key) { - $children_types[] = $element[$key]['#type']; - } - if (!in_array('chart_xaxis', $children_types)) { - $element['xaxis'] = array('#type' => 'chart_xaxis'); - } - if (!in_array('chart_yaxis', $children_types)) { - $element['yaxis'] = array('#type' => 'chart_yaxis'); - } - } + $view = $variables['view']; - // Convert integer properties to save library modules the hassle. - charts_cast_element_integer_values($element); + $categoriesAttachment = array(); + $seriesDataAttachment = array(); - // Generic theme function assuming it will be suitable for most chart types. - $element['#theme'] = 'charts_chart'; + for ($i = 0; $i < count($attachmentView); $i++) { - // Allow the chart to be altered. - $alter_hooks = array('chart'); - if ($element['#chart_id']) { - $alter_hooks[] = 'chart_' . $element['#chart_id']; - } - drupal_alter($alter_hooks, $element, $element['#chart_id']); + $attachmentId = $attachmentView[$i]->display_handler->display['id']; - // Include the library specific file and render via their callback. - if (isset($chart_library_info['file'])) { - $module_path = drupal_get_path('module', $chart_library_info['module']); - include_once $module_path . '/' . $chart_library_info['file']; - } - $callback = $chart_library_info['render']; - $element = $callback($element); + $attachmentDisplay = $view->storage->getDisplay($attachmentId); + $attachedValueField = $attachmentDisplay['display_options']['style']['options']['data_fields']; - if (!empty($element['#chart_definition'])) { - $chart_definition = $element['#chart_definition']; - unset($element['#chart_definition']); + $combinedAttachmentPage = Util::removeUnselectedFields($attachedValueField); + $attachmentColor = $attachmentView[$i]->style_plugin->options['field_colors']; + $labelField = $attachmentView[$i]->style_plugin->options['label_field']; + $dataAttachment = Util::viewsData($attachmentView[$i], $combinedAttachmentPage, $labelField, $attachmentColor); + $dataAttachmentFormatted = Util::createChartableData($dataAttachment); - // Allow the chart definition to be altered. - $alter_hooks = array('chart_definition'); - if ($element['#chart_id']) { - $alter_hooks[] = 'chart_definition_' . $element['#chart_id']; + for ($j = 0; $j < count($dataAttachmentFormatted[0]); $j++) { + array_push($categoriesAttachment, $dataAttachmentFormatted[0][$j]); } - drupal_alter($alter_hooks, $chart_definition, $element, $element['#chart_id']); - // Set the element #chart_json property as a data-attribute. - $element['#attributes']['data-chart'] = drupal_json_encode($chart_definition); - } - - return $element; -} - -/** - * Retrieve a list of all charting libraries available. - * - * @see hook_charts_info() - */ -function charts_info() { - $charts_info = array(); - foreach (module_implements('charts_info') as $module) { - $module_charts_info = module_invoke($module, 'charts_info'); - foreach ($module_charts_info as $chart_library => $chart_library_info) { - $module_charts_info[$chart_library]['module'] = $module; - } - $charts_info = array_merge($charts_info, $module_charts_info); - } - drupal_alter('charts_info', $charts_info); - return $charts_info; -} - -/** - * Retrieve a specific chart library. - */ -function chart_get_library($library) { - $info = charts_info(); - return $info[$library] ? $info[$library] : FALSE; -} - -/** - * Retrieve a list of all chart types available. - * - * @see hook_charts_type_info() - */ -function charts_type_info() { - $charts_type_info = module_invoke_all('charts_type_info'); - // Populate defaults for all chart types. - foreach ($charts_type_info as $chart_type => $chart_type_info) { - $charts_type_info[$chart_type] += array( - 'label' => '', - 'axis' => CHARTS_DUAL_AXIS, - 'axis_inverted' => FALSE, - 'stacking' => FALSE, - ); - } - drupal_alter('charts_type_info', $charts_type_info); - return $charts_type_info; -} - -/** - * Retrieve a specific chart type. - */ -function chart_get_type($chart_type) { - $types = charts_type_info(); - return $types[$chart_type] ? $types[$chart_type] : FALSE; -} - -/** - * Implements hook_charts_type_info(). - */ -function charts_charts_type_info() { - $chart_types['pie'] = array( - 'label' => t('Pie'), - 'axis' => CHARTS_SINGLE_AXIS, - ); - $chart_types['bar'] = array( - 'label' => t('Bar'), - 'axis' => CHARTS_DUAL_AXIS, - 'axis_inverted' => TRUE, // Meaning x/y axis are flipped. - 'stacking' => TRUE, - ); - $chart_types['column'] = array( - 'label' => t('Column'), - 'axis' => CHARTS_DUAL_AXIS, - 'stacking' => TRUE, - ); - $chart_types['line'] = array( - 'label' => t('Line'), - 'axis' => CHARTS_DUAL_AXIS, - ); - $chart_types['area'] = array( - 'label' => t('Area'), - 'axis' => CHARTS_DUAL_AXIS, - 'stacking' => TRUE, - ); - $chart_types['scatter'] = array( - 'label' => t('Scatter'), - 'axis' => CHARTS_DUAL_AXIS, - ); - return $chart_types; -} - -/** - * Default colors used in all libraries. - */ -function charts_default_colors() { - return array( - '#2f7ed8', '#0d233a', '#8bbc21','#910000', '#1aadce', - '#492970', '#f28f43', '#77a1e5', '#c42525', '#a6c96a', - ); -} - -/** - * Recursive function to trim out empty options that aren't used. - */ -function charts_trim_array(&$array) { - foreach ($array as $key => &$value) { - if (is_array($value)) { - charts_trim_array($value); - } - elseif (is_null($value) || (is_array($value) && count($value) === 0)) { - unset($array[$key]); + for ($j = 0; $j < count($dataAttachmentFormatted[1]); $j++) { + array_push($seriesDataAttachment, $dataAttachmentFormatted[1][$j]); } } -} -/** - * Recursive function to cast integer values. - */ -function charts_cast_element_integer_values(&$element) { - // Cast options to integers to avoid redundant library fixing problems. - $integer_options = array( - // Chart options. - '#title_font_size', - '#font_size', - '#legend_title_font_size', - '#legend_font_size', - '#width', - '#height', - - // Axis options. - '#title_font_size', - '#labels_font_size', - '#labels_rotation', - '#max', - '#min', - - // Data options. - '#decimal_count', - ); - foreach ($element as $property_name => $value) { - if (is_array($element[$property_name])) { - charts_cast_element_integer_values($element[$property_name]); - } - elseif ($property_name && in_array($property_name, $integer_options)) { - $element[$property_name] = (is_null($element[$property_name]) || strlen($element[$property_name]) === 0) ? NULL : (int) $element[$property_name]; - } + $library = $view->style_plugin->options['library']; + $variables['data'] = array(); + $labelField = $view->style_plugin->options['label_field']; + $valueField = $view->style_plugin->options['data_fields']; + $valueField = Util::removeUnselectedFields($valueField); + $color = $view->style_plugin->options['field_colors']; + $data = Util::viewsData($view, $valueField, $labelField, $color); + $data = Util::createChartableData($data); + $categories = $data[0]; + $seriesData = $data[1]; + $categories = array_merge($categories, $categoriesAttachment); + $seriesData = array_merge($seriesData, $seriesDataAttachment); + + // Handles the toggling from one library to another. + switch ($library) { + case 'google': + $googleData = charts_google_render_charts($categories, $seriesData); + $googleOptions = charts_google_create_charts_options($options, $seriesData); + $googleChartType = charts_google_create_chart_type($options); + $variables['chart_type'] = 'google'; + $variables['attributes']['class'][0] = 'chart-google'; + $variables['content_attributes']['data-chart'][] = $googleData; + $variables['attributes']['google-options'][1] = json_encode($googleOptions); + $variables['attributes']['google-chart-type'][2] = json_encode($googleChartType); + + break; + + case 'highcharts': + $highchart = charts_highcharts_render_charts($options, $categories, $seriesData); + $variables['chart_type'] = 'highcharts'; + $variables['content_attributes']['data-chart'][] = json_encode($highchart); + $variables['attributes']['class'][] = 'charts-highchart'; + + break; + + case 'c3': + $c3 = charts_c3_render_charts($options, $categories, $seriesData); + $variables['chart_type'] = 'c3'; + $variables['content_attributes']['data-chart'][] = json_encode($c3); + $variables['attributes']['class'][] = 'charts-c3'; + break; + + default: + // Not handled. } } - - -/** - * Output the chart renderable as a string. - */ -function theme_charts_chart($variables) { - $element = $variables['element']; - - $attributes = $element['#attributes']; - $attributes['id'] = $element['#id']; - $attributes['class'][] = 'chart'; - - return '<div ' . drupal_attributes($attributes) . '>' . (isset($element['#chart']) ? $element['#chart'] : '') . '</div>'; -} diff --git a/charts.permissions.yml b/charts.permissions.yml new file mode 100644 index 0000000..1997d7e --- /dev/null +++ b/charts.permissions.yml @@ -0,0 +1,6 @@ +# In charts.permissions.yml file. +access all charts: + title: 'Administer Charts' + description: 'This permission needs to be fleshed out.' + restrict access: TRUE + diff --git a/charts.routing.yml b/charts.routing.yml new file mode 100644 index 0000000..bc4056b --- /dev/null +++ b/charts.routing.yml @@ -0,0 +1,7 @@ +charts.form: + path: '/admin/config/content/charts' + defaults: + _form: 'Drupal\charts\Form\ChartsConfigForm' + _title: 'Default chart configuration' + requirements: + _permission: 'administer site configuration' diff --git a/charts.services.yml b/charts.services.yml new file mode 100644 index 0000000..a948584 --- /dev/null +++ b/charts.services.yml @@ -0,0 +1,6 @@ +services: + charts.charts_service: + class: Drupal\charts\Services\ChartService + + charts.charts_attachment: + class: Drupal\charts\Services\ChartAttachmentService diff --git a/charts.settings.yml b/charts.settings.yml new file mode 100644 index 0000000..e69de29 diff --git a/charts.theme.inc b/charts.theme.inc new file mode 100644 index 0000000..8327b55 --- /dev/null +++ b/charts.theme.inc @@ -0,0 +1,38 @@ +<?php + +/** + * @file + * Theme for Charts views. + * @param $page + */ + +/** + * Hook attaches assests to pages depending on library selected + * + * @param $attachments + */ +/*function charts_page_attachments(&$attachments) +{ + $service = \Drupal::service('charts.charts_service'); + $library = $service->getLibrarySelected(); + /*Depending on the library selected, javescript files associated to the selected library will only be attached to + the template.*/ + +/* switch($library){ + case 'google': + $attachments['#attached']['library'][] = 'charts_google/charts_google'; + $attachments['#attached']['library'][] = 'charts_google/google'; + break; + case 'highcharts': + $attachments['#attached']['library'][] = 'charts_highcharts/charts_highcharts'; + $attachments['#attached']['library'][] = 'charts_highcharts/highcharts'; + break; + case 'c3': + $attachments['#attached']['library'][] = 'charts_c3/charts_c3'; + $attachments['#attached']['library'][] = 'charts_c3/c3'; + break; + + default: + + } +}*/ \ No newline at end of file diff --git a/includes/charts.examples.inc b/includes/charts.examples.inc deleted file mode 100644 index c4f423c..0000000 --- a/includes/charts.examples.inc +++ /dev/null @@ -1,274 +0,0 @@ -<?php - -/** - * Menu callback; Display all examples in the system. - */ -function charts_examples($library = NULL, $id = NULL) { - $functions = get_defined_functions(); - if ($library && $library_info = chart_get_library($library)) { - $charts_info = array($library => $library_info); - } - else { - $charts_info = charts_info(); - } - $table = array(); - foreach ($charts_info as $library => $chart_library_info) { - $table['header'][] = array( - 'width' => (1 / count($charts_info) * 100) . '%', - 'data' => $chart_library_info['label'], - ); - } - - $table['rows'] = array(); - foreach ($functions['user'] as $function) { - if (($id && '_charts_examples_' . $id === $function) || (!$id && strpos($function, '_charts_examples_') === 0)) { - $row = array(); - foreach ($charts_info as $library => $chart_library_info) { - $example = $function(); - $example['chart']['#chart_library'] = $library; - $example['chart']['#height'] = 200; - $notes = ''; - if (isset($example['notes'][$library])) { - $notes = '<p>' . t('Note') . ': ' . $example['notes'][$library] . '</p>'; - } - $row[] = array( - 'data' => drupal_render($example['chart']) . l(t('View'), 'charts/examples/built-in/' . $library . '/' . str_replace('_charts_examples_', '', $function)) . $notes, - 'valign' => 'top', - ); - } - $table['rows'][] = $row; - } - } - - return theme('table', $table); - -} - -function _charts_examples_pie_simple() { - $chart = array( - '#type' => 'chart', - '#title' => t('Pie simple'), - '#chart_type' => 'pie', - '#chart_library' => 'highcharts', - '#legend_position' => 'right', - '#data_labels' => TRUE, - '#tooltips' => FALSE, - ); - $chart['pie_data'] = array( - '#type' => 'chart_data', - '#title' => t('Gender'), - '#labels' => array('Male', 'Female'), - '#data' => array(10, 20), - ); - - $example['chart'] = $chart; - - return $example; -} - -function _charts_examples_pie_tooltips() { - $example = _charts_examples_pie_simple(); - $example['chart']['#title'] = t('Pie tooltips'); - $example['chart']['#tooltips'] = TRUE; - $example['chart']['#data_labels'] = FALSE; - return $example; -} - -function _charts_examples_pie_alternative_syntax() { - $chart = array( - '#type' => 'chart', - '#title' => t('Pie alternative syntax'), - '#chart_type' => 'pie', - '#chart_library' => 'highcharts', - '#legend_position' => 'right', - '#data_labels' => TRUE, - '#tooltips' => FALSE, - ); - $chart['pie_data'] = array( - '#type' => 'chart_data', - '#title' => t('Gender'), - '#data' => array(array('Male', 10), array('Female', 20)), - ); - - $example['chart'] = $chart; - - return $example; -} - -function _charts_examples_pie_data_overrides() { - $example = _charts_examples_pie_simple(); - $example['chart']['#title'] = t('Pie data overrides'); - $example['chart']['#tooltips'] = TRUE; - $example['chart']['pie_data'][0] = array( - '#type' => 'chart_data_item', - '#data' => 15, - '#color' => 'red', - '#title' => t('Dudes'), - ); - $example['chart']['pie_data'][1] = array( - '#type' => 'chart_data_item', - '#data' => 20, - '#title' => t('Chicks'), - ); - - $example['notes']['google'] = t('Google cannot assign a color to an individual item. See this <a href="https://code.google.com/p/google-visualization-api-issues/issues/detail?id=1267">feature request</a>.'); - - return $example; -} - -function _charts_examples_column_simple() { - $chart = array( - '#type' => 'chart', - '#chart_type' => 'column', - '#title' => t('Column simple'), - ); - $chart['male'] = array( - '#type' => 'chart_data', - '#title' => t('Male'), - '#data' => array(10, 20, 30), - '#suffix' => 'lbs', - ); - $chart['female'] = array( - '#type' => 'chart_data', - '#title' => t('Female'), - '#data' => array(12, 22, 32), - '#suffix' => 'lbs', - ); - $chart['xaxis'] = array( - '#type' => 'chart_xaxis', - '#labels' => array('Jan', 'Feb', 'Mar'), - ); - - $example['chart'] = $chart; - - return $example; -} - -function _charts_examples_column_stacking() { - $example = _charts_examples_column_simple(); - $example['chart']['#title'] = t('Column stacked'); - $example['chart']['#stacking'] = TRUE; - - return $example; -} - -function _charts_examples_bar_simple() { - $example = _charts_examples_column_simple(); - $example['chart']['#title'] = t('Bar simple'); - $example['chart']['#chart_type'] = 'bar'; - return $example; -} - -function _charts_examples_line_gap() { - $chart = array( - '#type' => 'chart', - '#chart_type' => 'line', - '#title' => t('Line with interpolated gap'), - ); - // Test with a gap in the data. - $chart['male'] = array( - '#type' => 'chart_data', - '#title' => t('Male'), - '#data' => array(array(0, 0), array(10, NULL), array(20, 30), array(30, 30), array(40, 40)), - ); - $chart['female'] = array( - '#type' => 'chart_data', - '#title' => t('Female'), - '#data' => array(array(0, 5), array(10, 14), array(20, 16), array(30, 36), array(40, 48)), - ); - - $example['chart'] = $chart; - - return $example; -} - -function _charts_examples_scatter() { - $chart = array( - '#type' => 'chart', - '#chart_type' => 'scatter', - '#title' => t('Scatter'), - ); - $chart['male'] = array( - '#type' => 'chart_data', - '#title' => t('Male'), - '#data' => array(array(10, 10), array(20, 20), array(30, 30)), - ); - $chart['female'] = array( - '#type' => 'chart_data', - '#title' => t('Female'), - '#data' => array(array(12, 12), array(20, 24), array(30, 36)), - ); - - $example['chart'] = $chart; - - return $example; -} - -function _charts_examples_combo() { - $chart = array( - '#type' => 'chart', - '#chart_type' => 'column', - '#title' => t('Combo'), - '#legend_position' => 'bottom', - ); - $chart['male'] = array( - '#type' => 'chart_data', - '#title' => t('Male'), - '#data' => array(10, 20, 30), - '#suffix' => 'lbs', - ); - $chart['female'] = array( - '#type' => 'chart_data', - '#title' => t('Female'), - '#data' => array(12, 22, 32), - '#suffix' => 'lbs', - ); - $chart['female'][0] = array( - '#type' => 'chart_data_item', - '#title' => t('Special title'), - '#color' => 'red', - '#data' => 22, - ); - - $secondary_color = '#B617E5'; - $chart['line'] = array( - '#type' => 'chart_data', - '#chart_type' => 'line', - '#data' => array(7, 44, 100), - '#title' => t('Average'), - '#target_axis' => 'yaxis2', - '#color' => $secondary_color, - //'#marker_radius' => 10, - '#prefix' => '$', - ); - $chart['line'][1] = array( - '#type' => 'chart_data_item', - //'#color' => 'red', - //'#radius' => 10, - ); - - $chart['xaxis'] = array( - '#type' => 'chart_xaxis', - '#labels' => array('Jan', 'Feb', 'Mar'), - ); - - $chart['yaxis'] = array( - '#type' => 'chart_yaxis', - '#axis_type' => 'linear', - ); - - $chart['yaxis2'] = array( - '#type' => 'chart_yaxis', - '#axis_type' => 'linear', - '#opposite' => TRUE, - '#title' => t('Avg'), - '#labels_color' => $secondary_color, - '#title_color' => $secondary_color, - ); - - $example['chart'] = $chart; - - $example['notes']['google'] = t('Google charts cannot provide a legend on the same side as an axis, so legends cannot be displayed on the left or right in a combo chart.') . ' ' . t('Google cannot assign a color to an individual item. See this <a href="https://code.google.com/p/google-visualization-api-issues/issues/detail?id=1267">feature request</a>.'); - - return $example; -} diff --git a/includes/charts.pages.inc b/includes/charts.pages.inc index 88c28fd..737e439 100644 --- a/includes/charts.pages.inc +++ b/includes/charts.pages.inc @@ -1,4 +1,5 @@ <?php + /** * @file * Menu callbacks for Charts module. @@ -9,21 +10,214 @@ * * @param $form * The form array to which this form will be added. - * @param $defaults + * @param array $defaults * An array of existing values which will be used to populate defaults. - * @param $field_options + * @param array $field_options * An array of key => value names of fields within this chart. - * @param $parents + * @param array $parents * If all the contents of this form should be parented under a particular * namespace, an array of parent names that will be prepended to each * element's #parents property. - * @return - * The form with the chart settings added. + * + * @return mixed The form with the chart settings added. + * The form with the chart settings added. + */ + +/** + * Used to define a single axis. + * + * Constant used in hook_charts_type_info() to declare chart types with a single + * axis. For example a pie chart only has a single dimension. + */ +define('CHARTS_SINGLE_AXIS', 'y_only'); + +/** + * Used to define a dual axis. + * + * Constant used in hook_charts_type_info() to declare chart types with a dual + * axes. Most charts use this type of data, meaning multiple categories each + * have multiple values. This type of data is usually represented as a table. + */ +define('CHARTS_DUAL_AXIS', 'xy'); + +// Store Charts preprocess theme functions in a separate .inc file. +\Drupal::moduleHandler()->loadInclude('charts', 'inc', 'charts.theme'); + +/** + * Retrieve a list of all charting libraries available. + * + * @see hook_charts_info() + */ +function charts_info() { + + $charts_info = array(); + $chart_modules = Drupal::moduleHandler()->getImplementations('charts_info'); + foreach ($chart_modules as $module) { + $module_charts_info = Drupal::moduleHandler() + ->invoke($module, 'charts_info'); + foreach ($module_charts_info as $chart_library => $chart_library_info) { + $module_charts_info[$chart_library]['module'] = $module; + } + $charts_info = array_merge($charts_info, $module_charts_info); + } + Drupal::moduleHandler()->alter('charts_info', $charts_info); + + return $charts_info; +} + +/** + * Retrieve a list of all chart types available. + * + * @see hook_charts_type_info() + */ +function charts_type_info() { + + $charts_type_info = Drupal::moduleHandler()->invokeAll('charts_type_info'); + + foreach ($charts_type_info as $chart_type => $chart_type_info) { + $charts_type_info[$chart_type] += array( + 'label' => '', + 'axis' => CHARTS_DUAL_AXIS, + 'axis_inverted' => FALSE, + 'stacking' => FALSE, + ); + } + + Drupal::moduleHandler()->alter('charts_type_info', $charts_type_info); + return $charts_type_info; +} + +/** + * Retrieve a specific chart type. + * + * @param string $chart_type + * The type of chart selected for display. + * + * @return mixed + * If not false, returns an array of values from charts_charts_type_info. + */ +function charts_get_type($chart_type) { + $types = charts_type_info(); + return ($types[$chart_type]) ? $types[$chart_type] : FALSE; +} + +/** + * Implements hook_charts_type_info(). + */ +function charts_charts_type_info() { + $chart_types['pie'] = array( + 'label' => t('Pie'), + 'axis' => CHARTS_SINGLE_AXIS, + ); + $chart_types['bar'] = array( + 'label' => t('Bar'), + 'axis' => CHARTS_DUAL_AXIS, + 'axis_inverted' => TRUE, + 'stacking' => TRUE, + ); + $chart_types['column'] = array( + 'label' => t('Column'), + 'axis' => CHARTS_DUAL_AXIS, + 'stacking' => TRUE, + ); + $chart_types['line'] = array( + 'label' => t('Line'), + 'axis' => CHARTS_DUAL_AXIS, + ); + $chart_types['area'] = array( + 'label' => t('Area'), + 'axis' => CHARTS_DUAL_AXIS, + 'stacking' => TRUE, + ); + $chart_types['scatter'] = array( + 'label' => t('Scatter'), + 'axis' => CHARTS_DUAL_AXIS, + ); + return $chart_types; +} + +/** + * Default colors used in all libraries. + */ +function charts_default_colors() { + return array( + '#2f7ed8', + '#0d233a', + '#8bbc21', + '#910000', + '#1aadce', + '#492970', + '#f28f43', + '#77a1e5', + '#c42525', + '#a6c96a', + ); +} + +/** + * Recursive function to trim out empty options that aren't used. + * + * @param array $array + * Array may contain empty options. + */ +function charts_trim_array(&$array) { + foreach ($array as $key => &$value) { + if (is_array($value)) { + charts_trim_array($value); + } + elseif (is_null($value) || (is_array($value) && count($value) === 0)) { + unset($array[$key]); + } + } +} + +/** + * Recursive function to cast integer values. + * + * @param mixed $element + * Cast options to integers to avoid redundant library fixing problems. + */ +function charts_cast_element_integer_values(&$element) { + $integer_options = array( + // Chart options. + '#title_font_size', + '#font_size', + '#legend_title_font_size', + '#legend_font_size', + '#width', + '#height', + // Axis options. + '#title_font_size', + '#labels_font_size', + '#labels_rotation', + '#max', + '#min', + // Data options. + '#decimal_count', + ); + + foreach ($element as $property_name => $value) { + if (is_array($element[$property_name])) { + charts_cast_element_integer_values($element[$property_name]); + } + elseif ($property_name && in_array($property_name, $integer_options)) { + $element[$property_name] = (is_null($element[$property_name]) || strlen($element[$property_name]) === 0) + ? NULL : (int) $element[$property_name]; + } + } +} + +/** + * @param $form + * @param array $defaults + * @param array $field_options + * @param array $parents + * + * @return mixed */ function charts_settings_form($form, $defaults = array(), $field_options = array(), $parents = array()) { // Ensure all defaults are set. $options = array_merge(charts_default_settings(), $defaults); - $form['#attached']['library'][] = array('charts', 'charts.admin'); // Get a list of available chart libraries. @@ -56,7 +250,12 @@ function charts_settings_form($form, $defaults = array(), $field_options = array '#options' => $type_options, '#required' => TRUE, '#weight' => -20, - '#attributes' => array('class' => array('chart-type-radios', 'container-inline')), + '#attributes' => array( + 'class' => array( + 'chart-type-radios', + 'container-inline', + ), + ), '#parents' => array_merge($parents, array('type')), ); @@ -72,8 +271,13 @@ function charts_settings_form($form, $defaults = array(), $field_options = array if ($field_options) { $first_field = key($field_options); - $field_keys = array_diff($field_options, array($first_field => NULL)); - $form['fields']['#theme'] = 'charts_settings_fields'; + + $form['#theme'] = 'charts_settings_fields'; + + $form['fields'] = array( + '#title' => t('Charts fields'), + '#type' => 'fieldset', + ); $form['fields']['label_field'] = array( '#type' => 'radios', '#title' => t('Label field'), @@ -82,29 +286,48 @@ function charts_settings_form($form, $defaults = array(), $field_options = array '#weight' => -10, '#parents' => array_merge($parents, array('label_field')), ); - $form['fields']['data_fields'] = array( - '#type' => 'checkboxes', - '#title' => t('Data fields'), - '#options' => $field_options, - '#default_value' => isset($options['data_fields']) ? $options['data_fields'] : array_diff(array_keys($field_options), array($first_field)), - '#weight' => -9, - '#parents' => array_merge($parents, array('data_fields')), + $form['fields']['table'] = array( + '#type' => 'table', + '#header' => array(t('Field Name'), t('Provides Data'), t('Color')), + '#tabledrag' => TRUE, ); - $color_count = 0; + + $field_count = 0; foreach ($field_options as $field_name => $field_label) { - $form['fields']['field_colors'][$field_name] = array( + $form['fields']['table'][$field_count]['label_label'] = array( + '#type' => 'label', + '#title' => $field_label, + '#column' => 'one', + ); + $form['fields']['table'][$field_count]['data_fields'][$field_name] = array( + '#type' => 'checkbox', + '#title' => $field_name, + '#default_value' => $options['data_fields'][$field_name], + '#return_value' => $field_name, + '#weight' => -9, + '#states' => array( + 'disabled' => array( + ':input[name="style_options[label_field]"]' => array('value' => $field_name), + ), + ), + '#parents' => array_merge($parents, array('data_fields', $field_name)), + '#column' => 'two', + ); + $form['fields']['table'][$field_count]['field_colors'][$field_name] = array( '#type' => 'textfield', '#attributes' => array('TYPE' => 'color'), '#size' => 10, '#maxlength' => 7, '#theme_wrappers' => array(), - '#default_value' => !empty($options['field_colors'][$field_name]) ? $options['field_colors'][$field_name] : $options['colors'][$color_count], + '#default_value' => !empty($options['field_colors'][$field_name]) ? $options['field_colors'][$field_name] : $options['colors'][$field_count], '#parents' => array_merge($parents, array('field_colors', $field_name)), + '#column' => 'three', ); - $color_count++; + $field_count++; + } - } + } $form['display'] = array( '#title' => t('Display'), '#type' => 'fieldset', @@ -167,24 +390,6 @@ function charts_settings_form($form, $defaults = array(), $field_options = array '#parents' => array_merge($parents, array('legend_position')), ); - $form['display']['colors'] = array( - '#title' => t('Chart colors'), - '#theme_wrappers' => array('form_element'), - '#prefix' => '<div class="chart-colors">', - '#suffix' => '</div>', - ); - for ($color_count = 0; $color_count < 10; $color_count++) { - $form['display']['colors'][$color_count] = array( - '#type' => 'textfield', - '#attributes' => array('TYPE' => 'color'), - '#size' => 10, - '#maxlength' => 7, - '#theme_wrappers' => array(), - '#suffix' => ' ', - '#default_value' => $options['colors'][$color_count], - '#parents' => array_merge($parents, array('colors', $color_count)), - ); - } $form['display']['background'] = array( '#title' => t('Background color'), '#type' => 'textfield', @@ -203,7 +408,13 @@ function charts_settings_form($form, $defaults = array(), $field_options = array ); $form['display']['dimensions']['width'] = array( '#type' => 'textfield', - '#attributes' => array('TYPE' => 'number', 'step' => 1, 'min' => 0, 'max' => 9999, 'placeholder' => t('auto')), + '#attributes' => array( + 'TYPE' => 'number', + 'step' => 1, + 'min' => 0, + 'max' => 9999, + 'placeholder' => t('auto'), + ), '#default_value' => $options['width'], '#size' => 8, '#suffix' => ' x ', @@ -212,7 +423,13 @@ function charts_settings_form($form, $defaults = array(), $field_options = array ); $form['display']['dimensions']['height'] = array( '#type' => 'textfield', - '#attributes' => array('TYPE' => 'number', 'step' => 1, 'min' => 0, 'max' => 9999, 'placeholder' => t('auto')), + '#attributes' => array( + 'TYPE' => 'number', + 'step' => 1, + 'min' => 0, + 'max' => 9999, + 'placeholder' => t('auto'), + ), '#default_value' => $options['height'], '#size' => 8, '#suffix' => ' px', @@ -237,11 +454,11 @@ function charts_settings_form($form, $defaults = array(), $field_options = array '#title' => t('Labels rotation'), '#type' => 'select', '#options' => array( - 0 => '0°', - 30 => '30°', - 45 => '45°', - 60 => '60°', - 90 => '90°', + 0 => t('0°'), + 30 => t('30°'), + 45 => t('45°'), + 60 => t('60°'), + 90 => t('90°'), ), // This is only shown on non-inverted charts. '#attributes' => array('class' => array('axis-inverted-hide')), @@ -268,7 +485,11 @@ function charts_settings_form($form, $defaults = array(), $field_options = array ); $form['yaxis']['minmax']['min'] = array( '#type' => 'textfield', - '#attributes' => array('TYPE' => 'number', 'max' => 999999, 'placeholder' => t('Minimum')), + '#attributes' => array( + 'TYPE' => 'number', + 'max' => 999999, + 'placeholder' => t('Minimum'), + ), '#default_value' => $options['yaxis_min'], '#size' => 12, '#parents' => array_merge($parents, array('yaxis_min')), @@ -277,7 +498,11 @@ function charts_settings_form($form, $defaults = array(), $field_options = array ); $form['yaxis']['minmax']['max'] = array( '#type' => 'textfield', - '#attributes' => array('TYPE' => 'number', 'max' => 999999, 'placeholder' => t('Maximum')), + '#attributes' => array( + 'TYPE' => 'number', + 'max' => 999999, + 'placeholder' => t('Maximum'), + ), '#default_value' => $options['yaxis_max'], '#size' => 12, '#parents' => array_merge($parents, array('yaxis_max')), @@ -300,7 +525,13 @@ function charts_settings_form($form, $defaults = array(), $field_options = array $form['yaxis']['decimal_count'] = array( '#title' => t('Decimal count'), '#type' => 'textfield', - '#attributes' => array('TYPE' => 'number', 'step' => 1, 'min' => 0, 'max' => 20, 'placeholder' => t('auto')), + '#attributes' => array( + 'TYPE' => 'number', + 'step' => 1, + 'min' => 0, + 'max' => 20, + 'placeholder' => t('auto'), + ), '#default_value' => $options['yaxis_decimal_count'], '#size' => 5, '#description' => t('Enforce a certain number of decimal-place digits in displayed values.'), @@ -310,11 +541,11 @@ function charts_settings_form($form, $defaults = array(), $field_options = array '#title' => t('Labels rotation'), '#type' => 'select', '#options' => array( - 0 => '0°', - 30 => '30°', - 45 => '45°', - 60 => '60°', - 90 => '90°', + 0 => t('0°'), + 30 => t('30°'), + 45 => t('45°'), + 60 => t('60°'), + 90 => t('90°'), ), // This is only shown on inverted charts. '#attributes' => array('class' => array('axis-inverted-show')), @@ -327,9 +558,17 @@ function charts_settings_form($form, $defaults = array(), $field_options = array /** * Menu callback; Configure the site-wide defaults for charts. + * + * @param $form + * @param $form_state + * Standard parameters for a form. + * + * @return mixed + * */ function charts_default_settings_form($form, $form_state) { - $defaults = variable_get('charts_default_settings', array()); + $defaults = \Drupal::state() + ->get('charts_default_settings', array()); $defaults += charts_default_settings(); $field_options = array(); $parents = array('charts_default_settings'); @@ -368,11 +607,15 @@ function charts_default_settings_form($form, $form_state) { /** * Submit handler for charts_default_settings_form(). + * @param $form + * @param $form_state */ function charts_default_settings_form_submit($form, $form_state) { - variable_set('charts_default_settings', $form_state['values']['charts_default_settings']); + \Drupal::state() + ->set('charts_default_settings', $form_state['values']['charts_default_settings']); //was variable_set } + /** * Provides default options used by charts_settings_form(). */ @@ -406,56 +649,7 @@ function charts_default_settings() { $defaults['yaxis_decimal_count'] = ''; $defaults['yaxis_labels_rotation'] = 0; - drupal_alter('charts_default_settings', $defaults); + \Drupal::moduleHandler()->alter('charts_default_settings', $defaults); return $defaults; } -/** - * Output the table of field settings within the charts settings form. - */ -function theme_charts_settings_fields($variables) { - $element = $variables['element']; - - $table = array( - 'sticky' => FALSE, - ); - $table['header'] = array( - array('data' => t('Field name')), - array('data' => t('Provides labels'), 'class' => array('chart-label-field', 'checkbox')), - array('data' => t('Provides data'), 'class' => array('chart-data-field', 'checkbox')), - array('data' => t('Color'), 'class' => array('chart-field-color', 'checkbox')), - ); - foreach ($element['label_field']['#options'] as $field_name => $field_label) { - $element['label_field'][$field_name]['#title_display'] = 'attribute'; - $element['data_fields'][$field_name]['#title_display'] = 'attribute'; - $element['field_colors'][$field_name]['#title_display'] = 'attribute'; - - $row = array(); - $row[] = array( - 'data' => $field_label, - ); - $row[] = array( - 'data' => drupal_render($element['label_field'][$field_name]), - 'class' => array('chart-label-field', 'checkbox'), - ); - $row[] = array( - 'data' => $field_name ? drupal_render($element['data_fields'][$field_name]) : '', - 'class' => array('chart-data-field', 'checkbox'), - ); - $row[] = array( - 'data' => $field_name ? drupal_render($element['field_colors'][$field_name]) : '', - 'class' => array('chart-field-color', 'checkbox'), - ); - $table['rows'][] = $row; - } - - $wrapper = array( - '#title' => t('Chart fields'), - '#id' => 'chart-fields', - '#theme_wrappers' => array('form_element'), - '#markup' => theme('table', $table), - '#description' => t('Any field may be used to provide labels. Usually this is the first field configured in the view. e.g. Month names, types of content, etc. Subsequent fields typically provide numeric data which can be charted. In some chart types, no label field may be used at all to utilize a continuous axis.'), - ); - - return drupal_render($wrapper); -} diff --git a/js/charts.admin.js b/js/charts.admin.js index 86c188a..14b8c34 100644 --- a/js/charts.admin.js +++ b/js/charts.admin.js @@ -3,109 +3,111 @@ * Scripting for administrative interfaces of Charts module. */ (function ($) { + 'use strict'; -Drupal.behaviors.chartsAdmin = {}; -Drupal.behaviors.chartsAdmin.attach = function(context, settings) { - // Change options based on the chart type selected. - $(context).find('.form-radios.chart-type-radios').once('charts-axis-inverted', function() { + Drupal.behaviors.chartsAdmin = { + attach: function (context, settings) { + // Change options based on the chart type selected. + $(context).find('.form-radios.chart-type-radios').once('charts-axis-inverted', function () { - // Manually attach collapsible fieldsets first. - if (Drupal.behaviors.collapse) { - Drupal.behaviors.collapse.attach(context, settings); - } + // Manually attach collapsible fieldsets first. + if (Drupal.behaviors.collapse) { + Drupal.behaviors.collapse.attach(context, settings); + } - var xAxisLabel = $('fieldset.chart-xaxis .fieldset-title').html(); - var yAxisLabel = $('fieldset.chart-yaxis .fieldset-title').html(); + var xAxisLabel = $('fieldset.chart-xaxis .fieldset-title').html(); + var yAxisLabel = $('fieldset.chart-yaxis .fieldset-title').html(); - $(this).find('input:radio').change(function() { - if ($(this).is(':checked')) { - var groupingField = $(this).closest('form').find('.charts-grouping-field').val(); + $(this).find('input:radio').change(function () { + if ($(this).is(':checked')) { + var groupingField = $(this).closest('form').find('.charts-grouping-field').val(); - // Flip X/Y axis fieldset labels for inverted chart types. - if ($(this).attr('data-axis-inverted')) { - $('fieldset.chart-xaxis .fieldset-title').html(yAxisLabel); - $('fieldset.chart-xaxis .axis-inverted-show').closest('.form-item').show(); - $('fieldset.chart-xaxis .axis-inverted-hide').closest('.form-item').hide(); - $('fieldset.chart-yaxis .fieldset-title').html(xAxisLabel); - $('fieldset.chart-yaxis .axis-inverted-show').closest('.form-item').show(); - $('fieldset.chart-yaxis .axis-inverted-hide').closest('.form-item').hide(); - } - else { - $('fieldset.chart-xaxis .fieldset-title').html(xAxisLabel); - $('fieldset.chart-xaxis .axis-inverted-show').closest('.form-item').hide(); - $('fieldset.chart-xaxis .axis-inverted-hide').closest('.form-item').show(); - $('fieldset.chart-yaxis .fieldset-title').html(yAxisLabel); - $('fieldset.chart-yaxis .axis-inverted-show').closest('.form-item').hide(); - $('fieldset.chart-yaxis .axis-inverted-hide').closest('.form-item').show(); - } + // Flip X/Y axis fieldset labels for inverted chart types. + if ($(this).attr('data-axis-inverted')) { + $('fieldset.chart-xaxis .fieldset-title').html(yAxisLabel); + $('fieldset.chart-xaxis .axis-inverted-show').closest('.form-item').show(); + $('fieldset.chart-xaxis .axis-inverted-hide').closest('.form-item').hide(); + $('fieldset.chart-yaxis .fieldset-title').html(xAxisLabel); + $('fieldset.chart-yaxis .axis-inverted-show').closest('.form-item').show(); + $('fieldset.chart-yaxis .axis-inverted-hide').closest('.form-item').hide(); + } + else { + $('fieldset.chart-xaxis .fieldset-title').html(xAxisLabel); + $('fieldset.chart-xaxis .axis-inverted-show').closest('.form-item').hide(); + $('fieldset.chart-xaxis .axis-inverted-hide').closest('.form-item').show(); + $('fieldset.chart-yaxis .fieldset-title').html(yAxisLabel); + $('fieldset.chart-yaxis .axis-inverted-show').closest('.form-item').hide(); + $('fieldset.chart-yaxis .axis-inverted-hide').closest('.form-item').show(); + } - // Show color options for single axis settings. - if ($(this).attr('data-axis-single')) { - $('fieldset.chart-xaxis').hide(); - $('fieldset.chart-yaxis').hide(); - $('th.chart-field-color, td.chart-field-color').hide(); - $('div.chart-colors').show(); - } - else { - $('fieldset.chart-xaxis').show(); - $('fieldset.chart-yaxis').show(); - if (groupingField) { - $('th.chart-field-color, td.chart-field-color').hide(); - $('div.chart-colors').show(); - } - else { - $('th.chart-field-color, td.chart-field-color').show(); - $('div.chart-colors').hide(); + // Show color options for single axis settings. + if ($(this).attr('data-axis-single')) { + $('fieldset.chart-xaxis').hide(); + $('fieldset.chart-yaxis').hide(); + $('th.chart-field-color, td.chart-field-color').hide(); + $('div.chart-colors').show(); + } + else { + $('fieldset.chart-xaxis').show(); + $('fieldset.chart-yaxis').show(); + if (groupingField) { + $('th.chart-field-color, td.chart-field-color').hide(); + $('div.chart-colors').show(); + } + else { + $('th.chart-field-color, td.chart-field-color').show(); + $('div.chart-colors').hide(); + } + } } - } - } - }); - - // Set the initial values. - $(this).find('input:radio:checked').triggerHandler('change'); - }); + }); - // React to the setting of a group field. - $(context).find('.charts-grouping-field').once('charts-grouping', function() { - $(this).change(function() { - $form = $(this).closest('form'); + // Set the initial values. + $(this).find('input:radio:checked').triggerHandler('change'); + }); - // Hide the entire grouping field row, since no settings are applicable. - var value = $(this).val(); - $form.find('#chart-fields tr').show(); - if (value) { - var $labelField = $form.find('.chart-label-field input[value="' + value + '"]'); - $labelField.closest('tr').hide(); - if ($labelField.is(':checked')) { - $form.find('input[name="style_options[label_field]"][value=""]').attr('checked', 'checked').triggerHandler('change'); - } - } - // Restripe the table after hiding/showing rows. - $form.find('#chart-fields tr:visible') - .removeClass('odd even') - .filter(':even').addClass('odd').end() - .filter(':odd').addClass('even'); + // React to the setting of a group field. + $(context).find('.charts-grouping-field').once('charts-grouping', function () { + $(this).change(function () { + var $form; + $form = $(this).closest('form'); - // Recalculate shown color fields by triggering the chart type change. - $form.find('.form-radios.chart-type-radios input:radio:checked').triggerHandler('change'); - }).triggerHandler('change'); - }); + // Hide the entire grouping field row, since no settings are applicable. + var value = $(this).val(); + $form.find('#chart-fields tr').show(); + if (value) { + var $labelField = $form.find('.chart-label-field input[value="' + value + '"]'); + $labelField.closest('tr').hide(); + if ($labelField.is(':checked')) { + $form.find('input[name="style_options[label_field]"][value=""]').attr('checked', 'checked').triggerHandler('change'); + } + } + // Restripe the table after hiding/showing rows. + $form.find('#chart-fields tr:visible') + .removeClass('odd even') + .filter(':even').addClass('odd').end() + .filter(':odd').addClass('even'); - // Disable the data checkbox when a field is set as a label. - $(context).find('td.chart-label-field input').once('charts-axis-inverted', function() { - var $radio = $(this); - $radio.change(function() { - if ($radio.is(':checked')) { - $('.chart-data-field input').show(); - $('.chart-field-color input').show(); - $('input.chart-field-disabled').remove(); - $radio.closest('tr').find('.chart-data-field input').hide().after('<input type="checkbox" name="chart_field_disabled" disabled="disabled" class="chart-field-disabled" />'); - $radio.closest('tr').find('.chart-field-color input').hide(); - } - }); - $radio.triggerHandler('change'); - }); + // Recalculate shown color fields by triggering the chart type change. + $form.find('.form-radios.chart-type-radios input:radio:checked').triggerHandler('change'); + }).triggerHandler('change'); + }); -}; + // Disable the data checkbox when a field is set as a label. + $(context).find('td.chart-label-field input').once('charts-axis-inverted', function () { + var $radio = $(this); + $radio.change(function () { + if ($radio.is(':checked')) { + $('.chart-data-field input').show(); + $('.chart-field-color input').show(); + $('input.chart-field-disabled').remove(); + $radio.closest('tr').find('.chart-data-field input').hide().after('<input type="checkbox" name="chart_field_disabled" disabled="disabled" class="chart-field-disabled" />'); + $radio.closest('tr').find('.chart-field-color input').hide(); + } + }); + $radio.triggerHandler('change'); + }); -})(jQuery); \ No newline at end of file + } + }; +}(jQuery)); diff --git a/modules/charts_c3/charts_c3.info.yml b/modules/charts_c3/charts_c3.info.yml new file mode 100644 index 0000000..ee14a3f --- /dev/null +++ b/modules/charts_c3/charts_c3.info.yml @@ -0,0 +1,7 @@ +name: C3 Charts +type: module +description: 'Charts module integration with C3 Charts.' +package: Charts +core: 8.x +dependencies: + - charts:charts diff --git a/modules/charts_c3/charts_c3.libraries.yml b/modules/charts_c3/charts_c3.libraries.yml new file mode 100644 index 0000000..ab41b79 --- /dev/null +++ b/modules/charts_c3/charts_c3.libraries.yml @@ -0,0 +1,35 @@ +charts_c3: + version: 1.x + js: + js/charts_c3.js: {} + dependencies: + - core/jquery + - core/jquery.once + - core/drupal +d3: + remote: https://d3js.org/d3.v3.min.js + version: 3 + license: + name: BSD + url: https://en.wikipedia.org/wiki/BSD_licenses + gpl-compatible: false + js: + vendor/dthree/d3.v3.min.js: {} + +c3: + remote: https://cdnjs.cloudflare.com/ajax/libs/c3/0.4.11/c3.min.js + version: 0.4.11 + license: + name: MIT + url: https://opensource.org/licenses/MIT + gpl-compatible: true + css: + theme: + vendor/cthree/css/c3.min.css: {} + js: + vendor/cthree/c3.min.js: {} + dependencies: + - core/jquery + - core/jquery.once + - core/drupal + - charts_c3/d3 \ No newline at end of file diff --git a/modules/charts_c3/charts_c3.module b/modules/charts_c3/charts_c3.module new file mode 100644 index 0000000..fe3d703 --- /dev/null +++ b/modules/charts_c3/charts_c3.module @@ -0,0 +1,80 @@ +<?php +/** + * @file + * Charts module integration with C3 library. + */ +use Drupal\charts_c3\Settings\CThree\ChartType; +use Drupal\charts_c3\Settings\CThree\CThree; +use Drupal\charts_c3\Settings\CThree\ChartTitle; +use Drupal\charts_c3\Settings\CThree\ChartData; +use Drupal\charts_c3\Settings\CThree\ChartColor; +use Drupal\charts_c3\Settings\CThree\ChartAxis; + +/** + * Implements hook_charts_info(). + */ +function charts_c3_charts_info() { + $info['c3'] = array( + 'label' => t('C3 Charts'), + 'types' => array('area', 'bar', 'column', 'line', 'pie', 'scatter'), + ); + return $info; +} + +function charts_c3_render_charts($options, $categories = array(), $seriesData = array()) { + + $c3Data = array(); + for ($i = 0; $i < count($seriesData); $i++) { + $c3DataTemp = $seriesData[$i]['data']; + array_unshift($c3DataTemp, $seriesData[$i]['name']); + array_push($c3Data, $c3DataTemp); + } + + $c3Chart = new ChartType(); + $c3Chart->setType($options['type']); + $c3ChartTitle = new ChartTitle(); + $c3ChartTitle->setText($options['title']); + $chartAxis = new ChartAxis(); + + $c3 = new CThree(); + + //$c3->setChart($c3Chart); + //$c3->setLabels($options['data_labels']); + $c3->setTitle($c3ChartTitle); + $chartData = new ChartData(); + + $chartData->setType($options['type']); + $c3->setData($chartData); + if ($options['type'] == 'bar') { + $chartAxis->setRotated(TRUE); + array_unshift($categories, 'x'); + array_push($c3Data, $categories); + $chartData->setColumns($c3Data); + } + else { + if ($options['type'] == 'column') { + $chartData->setType('bar'); + $chartAxis->setRotated(FALSE); + array_unshift($categories, 'x'); + array_push($c3Data, $categories); + $chartData->setColumns($c3Data); + } + else { + $chartData->setColumns($c3Data); + } + } + + $c3->setAxis($chartAxis); + + $chartColor = new ChartColor(); + $seriesColors = array(); + for ($i = 0; $i < count($seriesData); $i++) { + $seriesColor = $seriesData[$i]['color']; + array_push($seriesColors, $seriesColor); + } + $chartColor->setPattern($seriesColors); + $c3->setColor($chartColor); + + + return $c3; +} diff --git a/modules/charts_c3/composer.json b/modules/charts_c3/composer.json new file mode 100644 index 0000000..b7068fb --- /dev/null +++ b/modules/charts_c3/composer.json @@ -0,0 +1,56 @@ +{ + "name": "charts_cthree", + "version": "0.0.1", + "require": { + "dthree": "*", + "cthree": "*", + "cthree/css": "*" + }, + "repositories": [ + { + "type": "package", + "package": { + "name": "dthree", + "version": "3", + "type": "dthree", + "dist": { + "url": "https://d3js.org/d3.v3.min.js", + "type": "file" + } + } + }, + { + "type": "package", + "package": { + "name": "cthree", + "version": "0.4.11", + "type": "cthree", + "dist": { + "url": "https://cdnjs.cloudflare.com/ajax/libs/c3/0.4.11/c3.min.js", + "type": "file" + } + } + }, + { + "type": "package", + "package": { + "name": "cthree/css", + "version": "0.4.11", + "type": "css", + "dist": { + "url": "https://cdnjs.cloudflare.com/ajax/libs/c3/0.4.11/c3.min.css", + "type": "file" + } + } + } + ], + "extra": { + "assets": { + "packages": { + "dthree": "*", + "cthree": "*", + "cthree/css": "*" + } + } + } +} diff --git a/modules/charts_c3/js/charts_c3.js b/modules/charts_c3/js/charts_c3.js new file mode 100644 index 0000000..0830593 --- /dev/null +++ b/modules/charts_c3/js/charts_c3.js @@ -0,0 +1,19 @@ +/** + * @file + * JavaScript integration between C3 and Drupal. + */ +(function ($) { + 'use strict'; + + Drupal.behaviors.chartsC3 = { + attach: function (context, settings) { + + $('.charts-c3').once().each(function () { + if ($(this).attr('data-chart')) { + var c3Chart = $('.charts-c3').attr('data-chart'); + c3.generate(JSON.parse(c3Chart)); + } + }); + } + }; +}(jQuery)); diff --git a/modules/charts_c3/src/Settings/CThree/CThree.php b/modules/charts_c3/src/Settings/CThree/CThree.php new file mode 100644 index 0000000..d2e7388 --- /dev/null +++ b/modules/charts_c3/src/Settings/CThree/CThree.php @@ -0,0 +1,84 @@ +<?php + +namespace Drupal\charts_c3\Settings\CThree; + +class CThree implements \JsonSerializable { + private $color; + private $bindto = '#chart'; + private $data; + private $axis; + private $title; + + /** + * @return mixed + */ + public function getTitle() { + return $this->title; + } + + /** + * @param mixed $title + */ + public function setTitle($title) { + $this->title = $title; + } + + /** + * @return mixed + */ + public function getAxis() { + return $this->axis; + } + + /** + * @param mixed $axis + */ + public function setAxis($axis) { + $this->axis = $axis; + } + + /** + * @return mixed + */ + public function getData() { + return $this->data; + } + + /** + * @param mixed $data + */ + public function setData($data) { + $this->data = $data; + } + + /** + * @return string + */ + public function getBindto() { + return $this->bindto; + } + + /** + * @return mixed + */ + public function getColor() { + return $this->color; + } + + /** + * @param mixed $color + */ + public function setColor($color) { + $this->color = $color; + } + + /** + * @return array + */ + public function jsonSerialize() { + $vars = get_object_vars($this); + + return $vars; + } + +} diff --git a/modules/charts_c3/src/Settings/CThree/ChartAxis.php b/modules/charts_c3/src/Settings/CThree/ChartAxis.php new file mode 100644 index 0000000..534906a --- /dev/null +++ b/modules/charts_c3/src/Settings/CThree/ChartAxis.php @@ -0,0 +1,32 @@ +<?php + +namespace Drupal\charts_c3\Settings\CThree; + +class ChartAxis implements \JsonSerializable { + private $rotated = FALSE; + private $x = array('type' => 'category'); + + /** + * @return mixed + */ + public function getRotated() { + return $this->rotated; + } + + /** + * @param mixed $rotated + */ + public function setRotated($rotated) { + $this->rotated = $rotated; + } + + /** + * @return array + */ + public function jsonSerialize() { + $vars = get_object_vars($this); + + return $vars; + } + +} diff --git a/modules/charts_c3/src/Settings/CThree/ChartColor.php b/modules/charts_c3/src/Settings/CThree/ChartColor.php new file mode 100644 index 0000000..9f881c3 --- /dev/null +++ b/modules/charts_c3/src/Settings/CThree/ChartColor.php @@ -0,0 +1,31 @@ +<?php + +namespace Drupal\charts_c3\Settings\CThree; + +class ChartColor implements \JsonSerializable { + private $pattern = array(); + + /** + * @return mixed + */ + public function getPattern() { + return $this->pattern; + } + + /** + * @param mixed $pattern + */ + public function setPattern($pattern) { + $this->pattern = $pattern; + } + + /** + * @return array + */ + public function jsonSerialize() { + $vars = get_object_vars($this); + + return $vars; + } + +} diff --git a/modules/charts_c3/src/Settings/CThree/ChartData.php b/modules/charts_c3/src/Settings/CThree/ChartData.php new file mode 100644 index 0000000..32a3150 --- /dev/null +++ b/modules/charts_c3/src/Settings/CThree/ChartData.php @@ -0,0 +1,76 @@ +<?php + +namespace Drupal\charts_c3\Settings\CThree; + +class ChartData implements \JsonSerializable { + private $columns = array(); + private $type; + private $labels = TRUE; + private $x = 'x'; + + /** + * @return array + */ + public function getX() { + return $this->x; + } + + /** + * @param array $x + */ + public function setX($x) { + $this->x = $x; + } + + /** + * @return array + */ + public function getColumns() { + return $this->columns; + } + + /** + * @param array $columns + */ + public function setColumns($columns) { + $this->columns = $columns; + } + + /** + * @return mixed + */ + public function getType() { + return $this->type; + } + + /** + * @param mixed $type + */ + public function setType($type) { + $this->type = $type; + } + + /** + * @return mixed + */ + public function getLabels() { + return $this->labels; + } + + /** + * @param mixed $labels + */ + public function setLabels($labels) { + $this->labels = $labels; + } + + /** + * @return array + */ + public function jsonSerialize() { + $vars = get_object_vars($this); + + return $vars; + } + +} diff --git a/modules/charts_c3/src/Settings/CThree/ChartDimensions.php b/modules/charts_c3/src/Settings/CThree/ChartDimensions.php new file mode 100644 index 0000000..1365904 --- /dev/null +++ b/modules/charts_c3/src/Settings/CThree/ChartDimensions.php @@ -0,0 +1,31 @@ +<?php + +namespace Drupal\charts_c3\Settings\CThree; + +class ChartDimensions implements \JsonSerializable { + private $ratio; + + /** + * @return mixed + */ + public function getRatio() { + return $this->ratio; + } + + /** + * @param mixed $ratio + */ + public function setRatio($ratio) { + $this->ratio = $ratio; + } + + /** + * @return array + */ + public function jsonSerialize() { + $vars = get_object_vars($this); + + return $vars; + } + +} diff --git a/modules/charts_c3/src/Settings/CThree/ChartLabel.php b/modules/charts_c3/src/Settings/CThree/ChartLabel.php new file mode 100644 index 0000000..ba521d5 --- /dev/null +++ b/modules/charts_c3/src/Settings/CThree/ChartLabel.php @@ -0,0 +1,31 @@ +<?php + +namespace Drupal\charts_c3\Settings\CThree; + +class ChartLabel implements \JsonSerializable { + private $rotation; + + /** + * @return mixed + */ + public function getRotation() { + return $this->rotation; + } + + /** + * @param mixed $rotation + */ + public function setRotation($rotation) { + $this->rotation = $rotation; + } + + /** + * @return array + */ + public function jsonSerialize() { + $vars = get_object_vars($this); + + return $vars; + } + +} diff --git a/modules/charts_c3/src/Settings/CThree/ChartTitle.php b/modules/charts_c3/src/Settings/CThree/ChartTitle.php new file mode 100644 index 0000000..65f38dd --- /dev/null +++ b/modules/charts_c3/src/Settings/CThree/ChartTitle.php @@ -0,0 +1,31 @@ +<?php + +namespace Drupal\charts_c3\Settings\CThree; + +class ChartTitle implements \JsonSerializable { + private $text; + + /** + * @return mixed + */ + public function getText() { + return $this->text; + } + + /** + * @param mixed $text + */ + public function setText($text) { + $this->text = $text; + } + + /** + * @return array + */ + public function jsonSerialize() { + $vars = get_object_vars($this); + + return $vars; + } + +} diff --git a/modules/charts_c3/src/Settings/CThree/ChartType.php b/modules/charts_c3/src/Settings/CThree/ChartType.php new file mode 100644 index 0000000..747d23b --- /dev/null +++ b/modules/charts_c3/src/Settings/CThree/ChartType.php @@ -0,0 +1,31 @@ +<?php + +namespace Drupal\charts_c3\Settings\CThree; + +class ChartType implements \JsonSerializable { + private $type; + + /** + * @return mixed + */ + public function getType() { + return $this->type; + } + + /** + * @param mixed $type + */ + public function setType($type) { + $this->type = $type; + } + + /** + * @return array + */ + public function jsonSerialize() { + $vars = get_object_vars($this); + + return $vars; + } + +} diff --git a/modules/charts_google/charts_google.inc b/modules/charts_google/charts_google.inc index 5359896..3cc36ab 100644 --- a/modules/charts_google/charts_google.inc +++ b/modules/charts_google/charts_google.inc @@ -1,4 +1,5 @@ <?php + /** * @file * Callbacks and utility functions for rendering a Google Chart. @@ -12,6 +13,7 @@ * * @param array $chart * The chart renderable. + * * @return * The modified chart renderable, with necessary #attached, #theme, and * similar properties prepared for rendering. @@ -131,13 +133,19 @@ function _charts_google_populate_chart_axes($chart, $chart_definition) { $axis['viewWindow']['min'] = strlen($chart[$key]['#min']) ? (int) $chart[$key]['#min'] : NULL; // Multi-axis support only applies to the major axis in Google charts. - $chart_type_info = chart_get_type($chart['#chart_type']); + $chart_type_info = charts_get_type($chart['#chart_type']); $axis_index = $chart[$key]['#opposite'] ? 1 : 0; if ($chart[$key]['#type'] === 'chart_xaxis') { - $axis_keys = !$chart_type_info['axis_inverted'] ? array('hAxis') : array('vAxes', $axis_index); + $axis_keys = !$chart_type_info['axis_inverted'] ? array('hAxis') : array( + 'vAxes', + $axis_index, + ); } else { - $axis_keys = !$chart_type_info['axis_inverted'] ? array('vAxes', $axis_index) : array('hAxis'); + $axis_keys = !$chart_type_info['axis_inverted'] ? array( + 'vAxes', + $axis_index, + ) : array('hAxis'); } $axis_drilldown = &$chart_definition['options']; foreach ($axis_keys as $key) { @@ -155,7 +163,7 @@ function _charts_google_populate_chart_axes($chart, $chart_definition) { */ function _charts_google_populate_chart_data(&$chart, $chart_definition) { $chart_definition['options']['series'] = array(); - $chart_type_info = chart_get_type($chart['#chart_type']); + $chart_type_info = charts_get_type($chart['#chart_type']); $series_number = 0; foreach (element_children($chart) as $key) { if ($chart[$key]['#type'] === 'chart_data') { @@ -226,7 +234,6 @@ function _charts_google_populate_chart_data(&$chart, $chart_definition) { // TODO: Convert this from PHP's date format to ICU format. // See https://developers.google.com/chart/interactive/docs/reference#dateformatter. //$series['_format']['dateFormat'] = $chart[$key]['#date_format']; - // Conveniently only the axis that supports multiple axes is the one that // can receive formatting, so we know that the key will always be plural. $axis_type = $chart_type_info['axis_inverted'] ? 'hAxes' : 'vAxes'; @@ -309,4 +316,5 @@ function _charts_google_populate_chart_data(&$chart, $chart_definition) { */ function _charts_google_escape_icu_characters($string) { return preg_replace('/([0-9@#\.\-,E\+;%\'\*])/', "'$1'", $string); + } diff --git a/modules/charts_google/charts_google.info b/modules/charts_google/charts_google.info deleted file mode 100644 index b49cd7a..0000000 --- a/modules/charts_google/charts_google.info +++ /dev/null @@ -1,5 +0,0 @@ -name = Google Charts -description = Charts module integration with Google Charts. -package = Charts -core = 7.x -dependencies[] = charts diff --git a/modules/charts_google/charts_google.info.yml b/modules/charts_google/charts_google.info.yml new file mode 100644 index 0000000..7d321e1 --- /dev/null +++ b/modules/charts_google/charts_google.info.yml @@ -0,0 +1,7 @@ +name: Google Charts +type: module +description: 'Charts module integration with Google Charts.' +package: Charts +core: 8.x +dependencies: + - charts:charts diff --git a/modules/charts_google/charts_google.js b/modules/charts_google/charts_google.js deleted file mode 100644 index bb74ba5..0000000 --- a/modules/charts_google/charts_google.js +++ /dev/null @@ -1,107 +0,0 @@ -/** - * @file - * JavaScript integration between Google Charts and Drupal. - */ -(function ($) { - -Drupal.behaviors.chartsGoogle = {}; -Drupal.behaviors.chartsGoogle.attach = function(context, settings) { - // First time loading in Views preview may not work because the Google JS - // API may not yet be loaded. - if (typeof google !== 'undefined') { - google.load('visualization', '1', { callback: renderCharts }); - } - - // Redraw charts on window resize. - var debounce; - $(window).resize(function() { - clearTimeout(debounce); - debounce = setTimeout(function() { - $('.charts-google').each(function() { - var wrap = $(this).data('chartsGoogleWrapper'); - if (wrap) { - wrap.draw(this); - } - }); - }, 75); - }); - - function renderCharts() { - $('.charts-google').once('charts-google', function() { - if ($(this).attr('data-chart')) { - var config = $.parseJSON($(this).attr('data-chart')); - var wrap = new google.visualization.ChartWrapper(); - wrap.setChartType(config.visualization); - wrap.setDataTable(config.data); - wrap.setOptions(config.options); - - // Apply data formatters. This only affects tooltips. The same format is - // already applied via the hAxis/vAxis.format option. - var dataTable = wrap.getDataTable(); - if (config.options.series) { - for (var n = 0; n < config.options.series.length; n++) { - if (config.options.series[n]['_format']) { - var format = config.options.series[n]['_format']; - if (format['dateFormat']) { - var formatter = new google.visualization.DateFormat({ pattern: format['dateFormat'] }); - } - else { - var formatter = new google.visualization.NumberFormat({ pattern: format['format'] }); - } - formatter.format(dataTable, n + 1); - } - } - } - - // Apply individual point properties, by adding additional "role" - // columns to the data table. So far this only applies "tooltip" - // properties to individual cells. Ideally, this would support "color" - // also. Feature request: - // https://code.google.com/p/google-visualization-api-issues/issues/detail?id=1267 - var columnsToAdd = {}; - var rowCount = dataTable.getNumberOfRows(); - var columnCount = dataTable.getNumberOfColumns(); - for (var rowIndex in config._data) { - if (config._data.hasOwnProperty(rowIndex)) { - for (var columnIndex in config._data[rowIndex]) { - if (config._data[rowIndex].hasOwnProperty(columnIndex)) { - for (var role in config._data[rowIndex][columnIndex]) { - if (config._data[rowIndex][columnIndex].hasOwnProperty(role)) { - if (!columnsToAdd[columnIndex]) { - columnsToAdd[columnIndex] = {}; - } - if (!columnsToAdd[columnIndex][role]) { - columnsToAdd[columnIndex][role] = new Array(rowCount); - } - columnsToAdd[columnIndex][role][rowIndex] = config._data[rowIndex][columnIndex][role]; - } - } - } - } - } - } - // Add columns from the right-most position. - for (var columnIndex = columnCount; columnIndex >= 0; columnIndex--) { - if (columnsToAdd[columnIndex]) { - for (var role in columnsToAdd[columnIndex]) { - if (columnsToAdd[columnIndex].hasOwnProperty(role)) { - dataTable.insertColumn(columnIndex + 1, { - type: 'string', - role: role, - }); - for (var rowIndex in columnsToAdd[columnIndex][role]) { - dataTable.setCell(parseInt(rowIndex) - 1, columnIndex + 1, columnsToAdd[columnIndex][role][rowIndex]); - } - } - } - } - } - - wrap.draw(this); - $(this).data('chartsGoogleWrapper', wrap); - } - }); - } -}; - -})(jQuery); diff --git a/modules/charts_google/charts_google.libraries.yml b/modules/charts_google/charts_google.libraries.yml new file mode 100644 index 0000000..72090a0 --- /dev/null +++ b/modules/charts_google/charts_google.libraries.yml @@ -0,0 +1,18 @@ +charts_google: + version: 1.x + js: + js/charts_google.js: {} + dependencies: + - core/jquery + - core/jquery.once + - core/drupal + +google: + remote: https://www.gstatic.com/charts/loader.js + version: VERSION + license: + name: Apache 2.0 + url: http://www.apache.org/licenses/LICENSE-2.0 + gpl-compatible: false + js: + vendor/google/loader.js: {} diff --git a/modules/charts_google/charts_google.module b/modules/charts_google/charts_google.module index 8992601..45aed4b 100644 --- a/modules/charts_google/charts_google.module +++ b/modules/charts_google/charts_google.module @@ -1,5 +1,14 @@ <?php +/** + * @file + * Charts module integration with Google Charts library. + */ + +use Drupal\charts_google\Settings\Google\GoogleOptions; +use Drupal\charts_google\Settings\Google\ChartType; +use Drupal\charts_google\Settings\Google\ChartArea; + /** * Implements hook_charts_info(). */ @@ -14,27 +23,72 @@ function charts_google_charts_info() { } /** - * Implements hook_library(). + * Creates a JSON Object formatted for Google charts to use + * @param array $categories + * @param array $seriesData + * + * @return json|string */ -function charts_google_library() { - $library['visualization'] = array( - 'title' => t('Google Visualization library'), - 'website' => 'https://google-developers.appspot.com/chart/', - 'version' => '1.0', - 'js' => array( - array('data' => '//www.google.com/jsapi', 'type' => 'external'), - ), - ); - $library['charts_google'] = array( - 'title' => t('Google Charts integration'), - 'version' => '1.0', - 'js' => array( - array('data' => drupal_get_path('module', 'charts_google') . '/charts_google.js', 'type' => 'file'), - ), - 'dependencies' => array( - array('charts_google', 'visualization'), - ), - ); +function charts_google_render_charts($categories = array(), $seriesData = array()) { + + $dataTable = array(); + + $dataTableHeader = array(); + for ($r = 0; $r < count($seriesData); $r++) { + array_push($dataTableHeader, $seriesData[$r]['name']); + } + + for ($j = 0; $j < count($categories); $j++) { + $rowDataTable = []; + for ($i = 0; $i < count($seriesData); $i++) { + $rowDataTabletemp = $seriesData[$i]['data'][$j]; + array_push($rowDataTable, $rowDataTabletemp); + } + array_unshift($rowDataTable, $categories[$j]); + array_push($dataTable, $rowDataTable); + } + + $dataTableHeader = array(); + for ($r = 0; $r < count($seriesData); $r++) { + array_push($dataTableHeader, $seriesData[$r]['name']); + } + + array_unshift($dataTableHeader, 'label'); + array_unshift($dataTable, $dataTableHeader); + + return json_encode($dataTable); +} + +/** + * @param $options + * @param array $seriesData + * @return GoogleOptions object with chart options or settings to be used by google visualization framework + */ +function charts_google_create_charts_options($options, $seriesData = array()) { + + $googleOptions = new GoogleOptions(); + $googleOptions->setTitle($options['title']); + $chartArea = new ChartArea(); + $chartArea->setWidth(400); + // $googleOptions->setChartArea($chartArea); + $seriesColors = array(); + for ($i = 0; $i < count($seriesData); $i++) { + $seriesColor = $seriesData[$i]['color']; + array_push($seriesColors, $seriesColor); + } + $googleOptions->setColors($seriesColors); + + return $googleOptions; +} + +/** + * @param $options + * @return ChartType + */ +function charts_google_create_chart_type($options) { + + $googleChartType = new ChartType(); + $googleChartType->setChartType($options['type']); - return $library; + return $googleChartType; } diff --git a/modules/charts_google/composer.json b/modules/charts_google/composer.json new file mode 100644 index 0000000..1f1934c --- /dev/null +++ b/modules/charts_google/composer.json @@ -0,0 +1,35 @@ +{ + "name": "charts_google", + "version": "0.0.1", + "require": { + "google": "*" + }, + "repositories": [ + { + "type": "package", + "package": { + "name": "google", + "version": "45", + "type": "google", + "dist": { + "url": "https://www.gstatic.com/charts/loader.js", + "type": "file" + } + } + } + ], + "extra": { + "assets": { + "actions": [ + { + "type": "copy", + "target": "js", + "pattern": "\\.js$" + } + ], + "packages": { + "google": "*" + } + } + } +} diff --git a/modules/charts_google/js/charts_google.js b/modules/charts_google/js/charts_google.js new file mode 100644 index 0000000..b381ffe --- /dev/null +++ b/modules/charts_google/js/charts_google.js @@ -0,0 +1,54 @@ +/** + * @file + * JavaScript integration between Google and Drupal. + */ +(function ($) { + 'use strict'; + + Drupal.behaviors.chartsGooglecharts = { + attach: function (context, settings) { + google.charts.load('current', {packages: ['corechart']}); + + var dataTable; + var googleChartOptions; + var googleChartType; + + $('.chart-google').once().each(function () { + if ($(this).attr('data-chart')) { + dataTable = $(this).attr('data-chart'); + googleChartOptions = $(this).attr('google-options'); + googleChartType = $(this).attr('google-chart-type'); + google.charts.setOnLoadCallback(drawChart); + } + }); + + + function drawChart() { + var data = google.visualization.arrayToDataTable(JSON.parse(dataTable)); + var googleChartTypeObject = JSON.parse(googleChartType); + var googleChartTypeFormatted = googleChartTypeObject.type; + var chart; + switch (googleChartTypeFormatted) { + case 'BarChart': + chart = new google.visualization.BarChart(document.getElementById('chart')); + break; + case 'ColumnChart': + chart = new google.visualization.ColumnChart(document.getElementById('chart')); + break; + case 'PieChart': + chart = new google.visualization.PieChart(document.getElementById('chart')); + break; + case 'ScatterChart': + chart = new google.visualization.ScatterChart(document.getElementById('chart')); + break; + case 'AreaChart': + chart = new google.visualization.AreaChart(document.getElementById('chart')); + break; + case 'LineChart': + chart = new google.visualization.LineChart(document.getElementById('chart')); + } + chart.draw(data, JSON.parse(googleChartOptions)); + } + } + }; +}(jQuery)); diff --git a/modules/charts_google/src/Settings/Google/ChartArea.php b/modules/charts_google/src/Settings/Google/ChartArea.php new file mode 100644 index 0000000..2aa3e67 --- /dev/null +++ b/modules/charts_google/src/Settings/Google/ChartArea.php @@ -0,0 +1,31 @@ +<?php + +namespace Drupal\charts_google\Settings\Google; + +class ChartArea implements \JsonSerializable { + private $width; + + /** + * @return mixed + */ + public function getWidth() { + return $this->width; + } + + /** + * @param mixed $width + */ + public function setWidth($width) { + $this->width = $width; + } + + /** + * @return array + */ + public function jsonSerialize() { + $vars = get_object_vars($this); + + return $vars; + } + +} diff --git a/modules/charts_google/src/Settings/Google/ChartType.php b/modules/charts_google/src/Settings/Google/ChartType.php new file mode 100644 index 0000000..972b143 --- /dev/null +++ b/modules/charts_google/src/Settings/Google/ChartType.php @@ -0,0 +1,32 @@ +<?php + +namespace Drupal\charts_google\Settings\Google; + +class ChartType implements \JsonSerializable { + private $type; + + /** + * @return mixed + */ + public function getChartType() { + return $this->type; + } + + /** + * @param mixed $type + */ + public function setChartType($type) { + $ucType = ucfirst($type); + $this->type = $ucType . 'Chart'; + } + + /** + * @return array + */ + public function jsonSerialize() { + $vars = get_object_vars($this); + + return $vars; + } + +} diff --git a/modules/charts_google/src/Settings/Google/GoogleOptions.php b/modules/charts_google/src/Settings/Google/GoogleOptions.php new file mode 100644 index 0000000..a0e80b3 --- /dev/null +++ b/modules/charts_google/src/Settings/Google/GoogleOptions.php @@ -0,0 +1,91 @@ +<?php + +namespace Drupal\charts_google\Settings\Google; + +class GoogleOptions implements \JsonSerializable { + private $title; + private $chartArea; + private $hAxis; + private $vAxis; + private $colors; + + /** + * @return mixed + */ + public function getTitle() { + return $this->title; + } + + /** + * @param mixed $title + */ + public function setTitle($title) { + $this->title = $title; + } + + /** + * @return mixed + */ + public function getChartArea() { + return $this->chartArea; + } + + /** + * @param mixed $chartArea + */ + public function setChartArea($chartArea) { + $this->chartArea = $chartArea; + } + + /** + * @return mixed + */ + public function getHAxis() { + return $this->hAxis; + } + + /** + * @param mixed $hAxis + */ + public function setHAxis($hAxis) { + $this->hAxis = $hAxis; + } + + /** + * @return mixed + */ + public function getVAxis() { + return $this->vAxis; + } + + /** + * @param mixed $vAxis + */ + public function setVAxis($vAxis) { + $this->vAxis = $vAxis; + } + + /** + * @return mixed + */ + public function getColors() { + return $this->colors; + } + + /** + * @param mixed $colors + */ + public function setColors($colors) { + $this->colors = $colors; + } + + /** + * @return array + */ + public function jsonSerialize() { + $vars = get_object_vars($this); + + return $vars; + } + +} diff --git a/modules/charts_google/src/Settings/Google/HorizontalAxis.php b/modules/charts_google/src/Settings/Google/HorizontalAxis.php new file mode 100644 index 0000000..7e5edf9 --- /dev/null +++ b/modules/charts_google/src/Settings/Google/HorizontalAxis.php @@ -0,0 +1,37 @@ +<?php + +namespace Drupal\charts_google\Settings\Google; + +class HorizontalAxis { + private $title; + private $minValue = 0; + + /** + * @return mixed + */ + public function getTitle() { + return $this->title; + } + + /** + * @param mixed $title + */ + public function setTitle($title) { + $this->title = $title; + } + + /** + * @return int + */ + public function getMinValue() { + return $this->minValue; + } + + /** + * @param int $minValue + */ + public function setMinValue($minValue) { + $this->minValue = $minValue; + } + +} diff --git a/modules/charts_google/src/Settings/Google/VerticalAxis.php b/modules/charts_google/src/Settings/Google/VerticalAxis.php new file mode 100644 index 0000000..d9a99ed --- /dev/null +++ b/modules/charts_google/src/Settings/Google/VerticalAxis.php @@ -0,0 +1,7 @@ +<?php + +namespace Drupal\charts_google\Settings\Google; + +class VerticalAxis { + private $title; +} diff --git a/modules/charts_highcharts/charts_highcharts.inc b/modules/charts_highcharts/charts_highcharts.inc index d63f226..6616f51 100644 --- a/modules/charts_highcharts/charts_highcharts.inc +++ b/modules/charts_highcharts/charts_highcharts.inc @@ -1,9 +1,13 @@ <?php + /** * @file * Callbacks and utility functions for rendering a Highcharts Chart. */ +use Drupal\Component\Utility\Html; +use Drupal\Component\Utility\NestedArray; + /** * Chart render callback; Convert all chart-level data. * @@ -12,9 +16,9 @@ * * @param array $chart * The chart renderable. - * @return - * The modified chart renderable, with necessary #attached, #theme, and - * similar properties prepared for rendering. + * @return array The modified chart renderable, with necessary #attached, #theme, and + * The modified chart renderable, with necessary #attached, #theme, and + * similar properties prepared for rendering. */ function _charts_highcharts_render($chart) { @@ -35,10 +39,13 @@ function _charts_highcharts_render($chart) { $chart_definition['series'] = $series; if (!isset($chart['#id'])) { - $chart['#id'] = drupal_html_id('highchart-render'); + $chart['#id'] = Html::getUniqueId('highchart-render'); } - $chart['#attached']['library'][] = array('charts_highcharts', 'charts_highcharts'); + $chart['#attached']['library'][] = array( + 'charts_highcharts', + 'charts_highcharts' + ); $chart['#attributes']['class'][] = 'charts-highchart'; $chart['#chart_definition'] = $chart_definition; @@ -47,6 +54,10 @@ function _charts_highcharts_render($chart) { /** * Utility to convert a Drupal renderable type to a Google visualization type. + * + * @param $renderable_type + * + * @return bool|mixed */ function _charts_highcharts_type($renderable_type) { $types = array( @@ -57,12 +68,15 @@ function _charts_highcharts_type($renderable_type) { 'pie_chart' => 'PieChart', 'scatter_chart' => 'ScatterChart', ); - drupal_alter('charts_highcharts_types', $types); + Drupal::moduleHandler()->alter('charts_highcharts_types', $types); return isset($types[$renderable_type]) ? $types[$renderable_type] : FALSE; } /** * Utility to populate main chart options. + * @param $chart + * @param $chart_definition + * @return mixed */ function _charts_highcharts_populate_chart_options($chart, $chart_definition) { $chart_definition['chart']['type'] = $chart['#chart_type']; @@ -72,7 +86,7 @@ function _charts_highcharts_populate_chart_options($chart, $chart_definition) { $chart_definition['title']['style']['fontStyle'] = $chart['#title_font_style']; $chart_definition['title']['style']['fontSize'] = $chart['#title_font_size']; $chart_definition['title']['verticalAlign'] = $chart['#title_position'] === 'in' ? 'top' : NULL; - $chart_definition['title']['y'] = $chart['#title_position'] === 'in' ? 24: NULL; + $chart_definition['title']['y'] = $chart['#title_position'] === 'in' ? 24 : NULL; $chart_definition['colors'] = $chart['#colors']; $chart_definition['chart']['style']['fontFamily'] = $chart['#font']; $chart_definition['chart']['style']['fontSize'] = $chart['#font_size']; @@ -117,7 +131,7 @@ function _charts_highcharts_populate_chart_options($chart, $chart_definition) { // Merge in chart raw options. if (isset($chart['#raw_options'])) { - $chart_definition = drupal_array_merge_deep($chart_definition, $chart['#raw_options']); + $chart_definition = NestedArray::mergeDeep($chart_definition, $chart['#raw_options']); } return $chart_definition; @@ -125,13 +139,17 @@ function _charts_highcharts_populate_chart_options($chart, $chart_definition) { /** * Utility to populate chart axes. + * @param $chart + * @param $chart_definition + * @return mixed */ function _charts_highcharts_populate_chart_axes($chart, $chart_definition) { - foreach (element_children($chart) as $key) { + foreach (\Drupal::state()->getMultiple($chart) as $key) { if ($chart[$key]['#type'] === 'chart_xaxis' || $chart[$key]['#type'] === 'chart_yaxis') { // Make sure defaults are loaded. if (empty($chart[$key]['#defaults_loaded'])) { - $chart[$key] += element_info($chart[$key]['#type']); + $chart[$key] += \Drupal::service('element_info') + ->getInfo($chart[$key]['#type']); } // Populate the chart data. @@ -161,7 +179,7 @@ function _charts_highcharts_populate_chart_axes($chart, $chart_definition) { // Highcharts. We want the label to be reasonably positioned on the // outside of the chart when labels are rotated. if ($axis['labels']['rotation']) { - $chart_type = chart_get_type($chart['#chart_type']); + $chart_type = charts_get_type($chart['#chart_type']); if ($axisType === 'xAxis' && !$chart_type['axis_inverted']) { $axis['labels']['align'] = 'left'; } @@ -176,7 +194,7 @@ function _charts_highcharts_populate_chart_axes($chart, $chart_definition) { // Merge in axis raw options. if (isset($chart[$key]['#raw_options'])) { - $axis = drupal_array_merge_deep($axis, $chart[$key]['#raw_options']); + $axis = NestedArray::mergeDeep($axis, $chart[$key]['#raw_options']); } $chart_definition[$axisType][] = $axis; @@ -188,24 +206,28 @@ function _charts_highcharts_populate_chart_axes($chart, $chart_definition) { /** * Utility to populate chart data. + * @param $chart + * @param $chart_definition + * @return mixed */ function _charts_highcharts_populate_chart_data(&$chart, $chart_definition) { $chart_definition['series'] = array(); - foreach (element_children($chart) as $key) { + foreach (\Drupal::state()->getMultiple($chart) as $key) { if ($chart[$key]['#type'] === 'chart_data') { $series = array(); $series_data = array(); // Make sure defaults are loaded. if (empty($chart[$key]['#defaults_loaded'])) { - $chart[$key] += element_info($chart[$key]['#type']); + $chart[$key] += \Drupal::service('element_info') + ->getInfo($chart[$key]['#type']); } // Convert target named axis keys to integers. if (isset($chart[$key]['#target_axis'])) { $axis_name = $chart[$key]['#target_axis']; $axis_index = 0; - foreach (element_children($chart) as $axis_key) { + foreach (\Drupal::state()->getMultiple($chart) as $axis_key) { if ($chart[$axis_key]['#type'] === 'chart_yaxis') { if ($axis_key === $axis_name) { break; @@ -259,19 +281,20 @@ function _charts_highcharts_populate_chart_data(&$chart, $chart_definition) { // Merge in series raw options. if (isset($chart[$key]['#raw_options'])) { - $series = drupal_array_merge_deep($series, $chart[$key]['#raw_options']); + $series = NestedArray::mergeDeep($series, $chart[$key]['#raw_options']); } // Add the series to the main chart definition. $chart_definition['series'][$key] = $series; // Merge in any point-specific data points. - foreach (element_children($chart[$key]) as $sub_key) { + foreach (\Drupal::state()->getMultiple($chart[$key]) as $sub_key) { if ($chart[$key][$sub_key]['#type'] === 'chart_data_item') { // Make sure defaults are loaded. if (empty($chart[$key][$sub_key]['#defaults_loaded'])) { - $chart[$key][$sub_key] += element_info($chart[$key][$sub_key]['#type']); + $chart[$key][$sub_key] += \Drupal::service('element_info') + ->getInfo($chart[$key][$sub_key]['#type']); } $data_item = $chart[$key][$sub_key]; @@ -311,7 +334,7 @@ function _charts_highcharts_populate_chart_data(&$chart, $chart_definition) { // Merge in point raw options. if (isset($data_item['#raw_options'])) { - $series_point = drupal_array_merge_deep($series_point, $data_item['#raw_options']); + $series_point = NestedArray::mergeDeep($series_point, $data_item['#raw_options']); } } } diff --git a/modules/charts_highcharts/charts_highcharts.info b/modules/charts_highcharts/charts_highcharts.info deleted file mode 100644 index 270b5ac..0000000 --- a/modules/charts_highcharts/charts_highcharts.info +++ /dev/null @@ -1,6 +0,0 @@ -name = Highcharts -description = Charts module integration with Highcharts library. -package = Charts -core = 7.x -dependencies[] = libraries -dependencies[] = charts diff --git a/modules/charts_highcharts/charts_highcharts.info.yml b/modules/charts_highcharts/charts_highcharts.info.yml new file mode 100644 index 0000000..b8205c4 --- /dev/null +++ b/modules/charts_highcharts/charts_highcharts.info.yml @@ -0,0 +1,7 @@ +name: Highcharts +type: module +description: 'Charts module integration with Highcharts library.' +package: Charts +core: 8.x +dependencies: + - charts:charts diff --git a/modules/charts_highcharts/charts_highcharts.install b/modules/charts_highcharts/charts_highcharts.install index 1cd7e0a..c5fce61 100644 --- a/modules/charts_highcharts/charts_highcharts.install +++ b/modules/charts_highcharts/charts_highcharts.install @@ -1,5 +1,7 @@ <?php + /** + * @file * Installation and update hooks for the Charts Highcharts module. */ @@ -8,17 +10,17 @@ */ function charts_highcharts_requirements($phase) { $requirements = array(); - $t = get_t(); if (function_exists('libraries_detect') && $highcharts_info = libraries_detect('highcharts')) { - if (is_dir($highcharts_info['library path'] . '/exporting-server')) { + if (is_dir($highcharts_info['library path'] . '/js/exporting-server')) { $requirements['highcharts_security'] = array( - 'title' => $t('Highcharts vulnerability'), + 'title' => t('Highcharts vulnerability'), 'severity' => REQUIREMENT_ERROR, - 'value' => $t('Dangerous sample code present'), - 'description' => $t('Your installation of the Highcharts library at "@path" contains a directory named "exporting-server". This directory contains dangerous sample files that may compromise the security of your site. You must delete this directory before you may use the Charts Highcharts module.', array('@path' => $highcharts_info['library path'])), + 'value' => t('Dangerous sample code present'), + 'description' => t('Your installation of the Highcharts library at "@path" contains a directory named "exporting-server". This directory contains dangerous sample files that may compromise the security of your site. You must delete this directory before you may use the Charts Highcharts module.', array('@path' => $highcharts_info['library path'])), ); } } return $requirements; + } diff --git a/modules/charts_highcharts/charts_highcharts.js b/modules/charts_highcharts/charts_highcharts.js deleted file mode 100644 index 5a6429a..0000000 --- a/modules/charts_highcharts/charts_highcharts.js +++ /dev/null @@ -1,17 +0,0 @@ -/** - * @file - * JavaScript integration between Highcharts and Drupal. - */ -(function ($) { - -Drupal.behaviors.chartsHighcharts = {}; -Drupal.behaviors.chartsHighcharts.attach = function(context, settings) { - $('.charts-highchart').once('charts-highchart', function() { - if ($(this).attr('data-chart')) { - var config = $.parseJSON($(this).attr('data-chart')); - $(this).highcharts(config); - } - }); -}; - -})(jQuery); diff --git a/modules/charts_highcharts/charts_highcharts.libraries.yml b/modules/charts_highcharts/charts_highcharts.libraries.yml new file mode 100644 index 0000000..03472b2 --- /dev/null +++ b/modules/charts_highcharts/charts_highcharts.libraries.yml @@ -0,0 +1,20 @@ +charts_highcharts: + version: VERSION + js: + js/charts_highcharts.js: {} + dependencies: + - core/jquery + - core/jquery.once + - core/drupal + +highcharts: + remote: https://code.highcharts.com/highcharts.js + version: VERSION + license: + name: Non-commercial + url: https://creativecommons.org/licenses/by-nc/3.0/ + gpl-compatible: false + js: + vendor/highcharts/highcharts.js: {} + dependencies: + - core/jquery diff --git a/modules/charts_highcharts/charts_highcharts.module b/modules/charts_highcharts/charts_highcharts.module index bd761ec..da2bf37 100644 --- a/modules/charts_highcharts/charts_highcharts.module +++ b/modules/charts_highcharts/charts_highcharts.module @@ -3,6 +3,20 @@ * @file * Charts module integration with Highcharts library. */ +use Drupal\charts_highcharts\Settings\Highcharts\ChartType; +use Drupal\charts_highcharts\Settings\Highcharts\ChartTitle; +use Drupal\charts_highcharts\Settings\Highcharts\Xaxis; +use Drupal\charts_highcharts\Settings\Highcharts\ChartLabel; +use Drupal\charts_highcharts\Settings\Highcharts\YaxisLabel; +use Drupal\charts_highcharts\Settings\Highcharts\Yaxis; +use Drupal\charts_highcharts\Settings\Highcharts\YaxisTitle; +use Drupal\charts_highcharts\Settings\Highcharts\DataLabelStatus; +use Drupal\charts_highcharts\Settings\Highcharts\DataLabels; +use Drupal\charts_highcharts\Settings\Highcharts\PlotOptions; +use Drupal\charts_highcharts\Settings\Highcharts\Tooltip; +use Drupal\charts_highcharts\Settings\Highcharts\ChartCredits; +use Drupal\charts_highcharts\Settings\Highcharts\ChartLegend; +use Drupal\charts_highcharts\Settings\Highcharts\Highcharts; /** * Implements hook_charts_info(). @@ -14,57 +28,62 @@ function charts_highcharts_charts_info() { 'types' => array('area', 'bar', 'column', 'line', 'pie', 'scatter'), 'file' => 'charts_highcharts.inc', ); + return $info; } /** - * Implements hook_library(). + * Creates a JSON Object formatted for Highcharts to use + * + * @param $options + * @param array $categories + * @param array $seriesData + * + * @return Highcharts object to be used by highcharts javascripts visualization framework */ -function charts_highcharts_library() { - if ($highcharts_info = libraries_detect('highcharts')) { - $library['highcharts'] = array( - 'title' => t('Highcharts library'), - 'website' => $highcharts_info['vendor url'], - 'version' => $highcharts_info['version'], - 'js' => array( - array('data' => $highcharts_info['library path'] . '/js/highcharts.js', 'type' => 'file'), - ), - ); +function charts_highcharts_render_charts($options, $categories = array(), $seriesData = array()) { + $chart = new ChartType(); + $chart->setType($options['type']); + $chartTitle = new ChartTitle(); + $chartTitle->setText($options['title']); + $chartXaxis = new Xaxis(); + $chartLabels = new ChartLabel(); + $chartLabels->setRotation($options['xaxis_labels_rotation']); + $chartXaxis->setCategories($categories); + $chartTitle->setText($options['title']); + $chartXaxis->setTitle($chartTitle); + $chartXaxis->setLabels($chartLabels); + $yaxisLabels = new YaxisLabel(); + $chartYaxis = new Yaxis(); + $yAxisTitle = new YaxisTitle(); + $yAxisTitle->setText($options['yaxis_title']); + if (!empty($options['yaxis_min'])){ + $chartYaxis->min = $options['yaxis_min']; } - $library['charts_highcharts'] = array( - 'title' => t('Highcharts integration'), - 'version' => '1.0', - 'js' => array( - array('data' => drupal_get_path('module', 'charts_highcharts') . '/charts_highcharts.js', 'type' => 'file'), - ), - 'dependencies' => array( - array('charts_highcharts', 'highcharts'), - ), - ); + if (!empty($options['yaxis_max'])){ + $chartYaxis->max = $options['yaxis_max']; + } + $chartYaxis->setLabels($yaxisLabels); + $chartYaxis->setTitle($yAxisTitle); + $dataLabelStatus = new DataLabelStatus(); + $dataLabels = new DataLabels(); + $dataLabels->setDataLabels($dataLabelStatus); + $barPlotOptns = new PlotOptions(); + $barPlotOptns->setBar($dataLabels); + $chartTooltip = new Tooltip(); + $chartCredits = new ChartCredits(); + $chartLegend = new ChartLegend(); - return $library; -} + $highchart = new Highcharts(); + $highchart->setChart($chart); + $highchart->setTitle($chartTitle); + $highchart->setXAxis($chartXaxis); + $highchart->setYAxis($chartYaxis); + $highchart->setTooltip($chartTooltip); + $highchart->setPlotOptions($barPlotOptns); + $highchart->setCredits($chartCredits); + $highchart->setLegend($chartLegend); + $highchart->setSeries($seriesData); -/** - * Implements hook_libraries_info(). - * - * Note that this is hook_libraries_info(), provided by libraries.module, rather - * than hook_library() provided by core. - */ -function charts_highcharts_libraries_info() { - $libraries['highcharts'] = array( - 'name' => t('Highcharts'), - 'vendor url' => 'http://www.highcharts.com', - 'download url' => 'http://www.highcharts.com/download', - 'version arguments' => array( - 'file' => 'highcharts.js', - // 3.x.x - 'pattern' => '/v(\d+\.\d+\.\d)/', - 'lines' => 5, - ), - 'files' => array( - 'js' => array('js/highcharts.js'), - ), - ); - return $libraries; + return $highchart; } diff --git a/modules/charts_highcharts/composer.json b/modules/charts_highcharts/composer.json new file mode 100644 index 0000000..299db28 --- /dev/null +++ b/modules/charts_highcharts/composer.json @@ -0,0 +1,35 @@ +{ + "name": "charts_highcharts", + "version": "0.0.1", + "require": { + "highcharts": "*" + }, + "repositories": [ + { + "type": "package", + "package": { + "name": "highcharts", + "version": "5.0.7", + "type": "highcharts", + "dist": { + "url": "https://code.highcharts.com/highcharts.js", + "type": "file" + } + } + } + ], + "extra": { + "assets": { + "actions": [ + { + "type": "copy", + "target": "js", + "pattern": "\\.js$" + } + ], + "packages": { + "highcharts": "*" + } + } + } +} diff --git a/modules/charts_highcharts/js/charts_highcharts.js b/modules/charts_highcharts/js/charts_highcharts.js new file mode 100644 index 0000000..36629f8 --- /dev/null +++ b/modules/charts_highcharts/js/charts_highcharts.js @@ -0,0 +1,19 @@ +/** + * @file + * JavaScript integration between Highcharts and Drupal. + */ +(function ($) { + 'use strict'; + + Drupal.behaviors.chartsHighcharts = { + attach: function (context, settings) { + + $('.charts-highchart').once().each(function () { + if ($(this).attr('data-chart')) { + var highcharts = $(this).attr('data-chart'); + $(this).highcharts(JSON.parse(highcharts)); + } + }); + } + }; +}(jQuery)); diff --git a/modules/charts_highcharts/src/Settings/Highcharts/ChartCredits.php b/modules/charts_highcharts/src/Settings/Highcharts/ChartCredits.php new file mode 100644 index 0000000..a75386c --- /dev/null +++ b/modules/charts_highcharts/src/Settings/Highcharts/ChartCredits.php @@ -0,0 +1,31 @@ +<?php + +namespace Drupal\charts_highcharts\Settings\Highcharts; + +class ChartCredits implements \JsonSerializable { + private $enabled = FALSE; + + /** + * @return boolean + */ + public function isEnabled() { + return $this->enabled; + } + + /** + * @param boolean $enabled + */ + public function setEnabled($enabled) { + $this->enabled = $enabled; + } + + /** + * @return array + */ + public function jsonSerialize() { + $vars = get_object_vars($this); + + return $vars; + } + +} diff --git a/modules/charts_highcharts/src/Settings/Highcharts/ChartLabel.php b/modules/charts_highcharts/src/Settings/Highcharts/ChartLabel.php new file mode 100644 index 0000000..ad7242c --- /dev/null +++ b/modules/charts_highcharts/src/Settings/Highcharts/ChartLabel.php @@ -0,0 +1,31 @@ +<?php + +namespace Drupal\charts_highcharts\Settings\Highcharts; + +class ChartLabel implements \JsonSerializable { + private $rotation; + + /** + * @return mixed + */ + public function getRotation() { + return $this->rotation; + } + + /** + * @param mixed $rotation + */ + public function setRotation($rotation) { + $this->rotation = (int) $rotation; + } + + /** + * @return array + */ + public function jsonSerialize() { + $vars = get_object_vars($this); + + return $vars; + } + +} diff --git a/modules/charts_highcharts/src/Settings/Highcharts/ChartLegend.php b/modules/charts_highcharts/src/Settings/Highcharts/ChartLegend.php new file mode 100644 index 0000000..3c90933 --- /dev/null +++ b/modules/charts_highcharts/src/Settings/Highcharts/ChartLegend.php @@ -0,0 +1,151 @@ +<?php + +namespace Drupal\charts_highcharts\Settings\Highcharts; + +class ChartLegend implements \JsonSerializable { + private $layout = 'vertical'; + private $align = 'right'; + private $verticalAlign = 'top'; + private $x = -40; + private $y = 80; + private $floating = TRUE; + private $borderWidth = 1; + private $backgroundColor = '#FCFFC5'; + private $shadow = TRUE; + + /** + * @return string + */ + public function getLayout() { + return $this->layout; + } + + /** + * @param string $layout + */ + public function setLayout($layout) { + $this->layout = $layout; + } + + /** + * @return string + */ + public function getAlign() { + return $this->align; + } + + /** + * @param string $align + */ + public function setAlign($align) { + $this->align = $align; + } + + /** + * @return string + */ + public function getVerticalAlign() { + return $this->verticalAlign; + } + + /** + * @param string $verticalAlign + */ + public function setVerticalAlign($verticalAlign) { + $this->verticalAlign = $verticalAlign; + } + + /** + * @return int + */ + public function getX() { + return $this->x; + } + + /** + * @param int $x + */ + public function setX($x) { + $this->x = $x; + } + + /** + * @return int + */ + public function getY() { + return $this->y; + } + + /** + * @param int $y + */ + public function setY($y) { + $this->y = $y; + } + + /** + * @return boolean + */ + public function isFloating() { + return $this->floating; + } + + /** + * @param boolean $floating + */ + public function setFloating($floating) { + $this->floating = $floating; + } + + /** + * @return int + */ + public function getBorderWidth() { + return $this->borderWidth; + } + + /** + * @param int $borderWidth + */ + public function setBorderWidth($borderWidth) { + $this->borderWidth = $borderWidth; + } + + /** + * @return string + */ + public function getBackgroundColor() { + return $this->backgroundColor; + } + + /** + * @param string $backgroundColor + */ + public function setBackgroundColor($backgroundColor) { + $this->backgroundColor = $backgroundColor; + } + + /** + * @return boolean + */ + public function isShadow() { + return $this->shadow; + } + + /** + * @param boolean $shadow + */ + public function setShadow($shadow) { + $this->shadow = $shadow; + } + + /** + * @return array + */ + public function jsonSerialize() { + $vars = get_object_vars($this); + + return $vars; + } + +} diff --git a/modules/charts_highcharts/src/Settings/Highcharts/ChartTitle.php b/modules/charts_highcharts/src/Settings/Highcharts/ChartTitle.php new file mode 100644 index 0000000..726c25c --- /dev/null +++ b/modules/charts_highcharts/src/Settings/Highcharts/ChartTitle.php @@ -0,0 +1,31 @@ +<?php + +namespace Drupal\charts_highcharts\Settings\Highcharts; + +class ChartTitle implements \JsonSerializable { + private $text; + + /** + * @return mixed + */ + public function getText() { + return $this->text; + } + + /** + * @param mixed $text + */ + public function setText($text) { + $this->text = $text; + } + + /** + * @return array + */ + public function jsonSerialize() { + $vars = get_object_vars($this); + + return $vars; + } + +} diff --git a/modules/charts_highcharts/src/Settings/Highcharts/ChartType.php b/modules/charts_highcharts/src/Settings/Highcharts/ChartType.php new file mode 100644 index 0000000..b0260bd --- /dev/null +++ b/modules/charts_highcharts/src/Settings/Highcharts/ChartType.php @@ -0,0 +1,31 @@ +<?php + +namespace Drupal\charts_highcharts\Settings\Highcharts; + +class ChartType implements \JsonSerializable { + private $type; + + /** + * @return mixed + */ + public function getType() { + return $this->type; + } + + /** + * @param mixed $type + */ + public function setType($type) { + $this->type = $type; + } + + /** + * @return array + */ + public function jsonSerialize() { + $vars = get_object_vars($this); + + return $vars; + } + +} diff --git a/modules/charts_highcharts/src/Settings/Highcharts/DataLabelStatus.php b/modules/charts_highcharts/src/Settings/Highcharts/DataLabelStatus.php new file mode 100644 index 0000000..3426d01 --- /dev/null +++ b/modules/charts_highcharts/src/Settings/Highcharts/DataLabelStatus.php @@ -0,0 +1,31 @@ +<?php + +namespace Drupal\charts_highcharts\Settings\Highcharts; + +class DataLabelStatus implements \JsonSerializable { + private $enabled = TRUE; + + /** + * @return boolean + */ + public function isEnabled() { + return $this->enabled; + } + + /** + * @param boolean $enabled + */ + public function setEnabled($enabled) { + $this->enabled = $enabled; + } + + /** + * @return array + */ + public function jsonSerialize() { + $vars = get_object_vars($this); + + return $vars; + } + +} diff --git a/modules/charts_highcharts/src/Settings/Highcharts/DataLabels.php b/modules/charts_highcharts/src/Settings/Highcharts/DataLabels.php new file mode 100644 index 0000000..080ef48 --- /dev/null +++ b/modules/charts_highcharts/src/Settings/Highcharts/DataLabels.php @@ -0,0 +1,31 @@ +<?php + +namespace Drupal\charts_highcharts\Settings\Highcharts; + +class DataLabels implements \JsonSerializable { + private $dataLabels; + + /** + * @return mixed + */ + public function getDataLabels() { + return $this->dataLabels; + } + + /** + * @param mixed $dataLabels + */ + public function setDataLabels($dataLabels) { + $this->dataLabels = $dataLabels; + } + + /** + * @return array + */ + public function jsonSerialize() { + $vars = get_object_vars($this); + + return $vars; + } + +} diff --git a/modules/charts_highcharts/src/Settings/Highcharts/Highcharts.php b/modules/charts_highcharts/src/Settings/Highcharts/Highcharts.php new file mode 100644 index 0000000..880a88e --- /dev/null +++ b/modules/charts_highcharts/src/Settings/Highcharts/Highcharts.php @@ -0,0 +1,152 @@ +<?php + +namespace Drupal\charts_highcharts\Settings\Highcharts; + +class Highcharts implements \JsonSerializable { + private $chart; + private $title; + private $xAxis; + private $yAxis; + private $tooltip; + private $plotOptions; + private $legend; + private $credits; + + /** + * @return mixed + */ + public function getChart() { + return $this->chart; + } + + /** + * @param mixed $chart + */ + public function setChart($chart) { + $this->chart = $chart; + } + + /** + * @return mixed + */ + public function getTitle() { + return $this->title; + } + + /** + * @param mixed $title + */ + public function setTitle($title) { + $this->title = $title; + } + + /** + * @return mixed + */ + public function getXAxis() { + return $this->xAxis; + } + + /** + * @param mixed $xAxis + */ + public function setXAxis($xAxis) { + $this->xAxis = $xAxis; + } + + /** + * @return mixed + */ + public function getYAxis() { + return $this->yAxis; + } + + /** + * @param mixed $yAxis + */ + public function setYAxis($yAxis) { + $this->yAxis = $yAxis; + } + + /** + * @return mixed + */ + public function getTooltip() { + return $this->tooltip; + } + + /** + * @param mixed $tooltip + */ + public function setTooltip($tooltip) { + $this->tooltip = $tooltip; + } + + /** + * @return mixed + */ + public function getPlotOptions() { + return $this->plotOptions; + } + + /** + * @param mixed $plotOptions + */ + public function setPlotOptions($plotOptions) { + $this->plotOptions = $plotOptions; + } + + /** + * @return mixed + */ + public function getLegend() { + return $this->legend; + } + + /** + * @param mixed $legend + */ + public function setLegend($legend) { + $this->legend = $legend; + } + + /** + * @return mixed + */ + public function getCredits() { + return $this->credits; + } + + /** + * @param mixed $credits + */ + public function setCredits($credits) { + $this->credits = $credits; + } + + /** + * @return mixed + */ + public function getSeries() { + return $this->series; + } + + /** + * @param mixed $series + */ + public function setSeries($series) { + $this->series = $series; + } + + private $series; + + /** + * @return array + */ + public function jsonSerialize() { + $vars = get_object_vars($this); + + return $vars; + } + +} diff --git a/modules/charts_highcharts/src/Settings/Highcharts/Label.php b/modules/charts_highcharts/src/Settings/Highcharts/Label.php new file mode 100644 index 0000000..dc94072 --- /dev/null +++ b/modules/charts_highcharts/src/Settings/Highcharts/Label.php @@ -0,0 +1,31 @@ +<?php + +namespace Drupal\charts_highcharts\Settings\Highcharts; + +class Label implements \JsonSerializable { + private $rotation; + + /** + * @return mixed + */ + public function getRotation() { + return $this->rotation; + } + + /** + * @param mixed $rotation + */ + public function setRotation($rotation) { + $this->rotation = $rotation; + } + + /** + * @return array + */ + public function jsonSerialize() { + $vars = get_object_vars($this); + + return $vars; + } + +} diff --git a/modules/charts_highcharts/src/Settings/Highcharts/PlotOptions.php b/modules/charts_highcharts/src/Settings/Highcharts/PlotOptions.php new file mode 100644 index 0000000..cf1d8a7 --- /dev/null +++ b/modules/charts_highcharts/src/Settings/Highcharts/PlotOptions.php @@ -0,0 +1,31 @@ +<?php + +namespace Drupal\charts_highcharts\Settings\Highcharts; + +class PlotOptions implements \JsonSerializable { + private $bar; + + /** + * @return mixed + */ + public function getBar() { + return $this->bar; + } + + /** + * @param mixed $bar + */ + public function setBar($bar) { + $this->bar = $bar; + } + + /** + * @return array + */ + public function jsonSerialize() { + $vars = get_object_vars($this); + + return $vars; + } + +} diff --git a/modules/charts_highcharts/src/Settings/Highcharts/Tooltip.php b/modules/charts_highcharts/src/Settings/Highcharts/Tooltip.php new file mode 100644 index 0000000..e96c678 --- /dev/null +++ b/modules/charts_highcharts/src/Settings/Highcharts/Tooltip.php @@ -0,0 +1,31 @@ +<?php + +namespace Drupal\charts_highcharts\Settings\Highcharts; + +class Tooltip implements \JsonSerializable { + private $valueSuffix = ''; + + /** + * @return string + */ + public function getValueSuffix() { + return $this->valueSuffix; + } + + /** + * @param string $valueSuffix + */ + public function setValueSuffix($valueSuffix) { + $this->valueSuffix = $valueSuffix; + } + + /** + * @return array + */ + public function jsonSerialize() { + $vars = get_object_vars($this); + + return $vars; + } + +} diff --git a/modules/charts_highcharts/src/Settings/Highcharts/Xaxis.php b/modules/charts_highcharts/src/Settings/Highcharts/Xaxis.php new file mode 100644 index 0000000..b532f40 --- /dev/null +++ b/modules/charts_highcharts/src/Settings/Highcharts/Xaxis.php @@ -0,0 +1,61 @@ +<?php + +namespace Drupal\charts_highcharts\Settings\Highcharts; + +class Xaxis implements \JsonSerializable { + private $categories = array(); + private $title; + private $labels; + + /** + * @return array + */ + public function getCategories() { + return $this->categories; + } + + /** + * @param array $categories + */ + public function setCategories($categories) { + $this->categories = $categories; + } + + /** + * @return mixed + */ + public function getTitle() { + return $this->title; + } + + /** + * @param mixed $title + */ + public function setTitle($title) { + $this->title = $title; + } + + /** + * @return mixed + */ + public function getLabels() { + return $this->labels; + } + + /** + * @param mixed $labels + */ + public function setLabels($labels) { + $this->labels = $labels; + } + + /** + * @return array + */ + public function jsonSerialize() { + $vars = get_object_vars($this); + + return $vars; + } + +} diff --git a/modules/charts_highcharts/src/Settings/Highcharts/Yaxis.php b/modules/charts_highcharts/src/Settings/Highcharts/Yaxis.php new file mode 100644 index 0000000..e282b5c --- /dev/null +++ b/modules/charts_highcharts/src/Settings/Highcharts/Yaxis.php @@ -0,0 +1,46 @@ +<?php + +namespace Drupal\charts_highcharts\Settings\Highcharts; + +class Yaxis implements \JsonSerializable { + private $title; + private $labels = ''; + + /** + * @return mixed + */ + public function getTitle() { + return $this->title; + } + + /** + * @param mixed $title + */ + public function setTitle($title) { + $this->title = $title; + } + + /** + * @return string + */ + public function getLabels() { + return $this->labels; + } + + /** + * @param string $labels + */ + public function setLabels($labels) { + $this->labels = $labels; + } + + /** + * @return array + */ + public function jsonSerialize() { + $vars = get_object_vars($this); + + return $vars; + } + +} diff --git a/modules/charts_highcharts/src/Settings/Highcharts/YaxisLabel.php b/modules/charts_highcharts/src/Settings/Highcharts/YaxisLabel.php new file mode 100644 index 0000000..b613b8e --- /dev/null +++ b/modules/charts_highcharts/src/Settings/Highcharts/YaxisLabel.php @@ -0,0 +1,31 @@ +<?php + +namespace Drupal\charts_highcharts\Settings\Highcharts; + +class YaxisLabel implements \JsonSerializable { + private $overflow = 'justify'; + + /** + * @param $overflow + */ + public function setOverflow($overflow) { + $this->overflow = $overflow; + } + + /** + * @return string + */ + public function getOverflow() { + return $this->overflow; + } + + /** + * @return array + */ + public function jsonSerialize() { + $vars = get_object_vars($this); + + return $vars; + } + +} diff --git a/modules/charts_highcharts/src/Settings/Highcharts/YaxisTitle.php b/modules/charts_highcharts/src/Settings/Highcharts/YaxisTitle.php new file mode 100644 index 0000000..964b062 --- /dev/null +++ b/modules/charts_highcharts/src/Settings/Highcharts/YaxisTitle.php @@ -0,0 +1,32 @@ +<?php + +namespace Drupal\charts_highcharts\Settings\Highcharts; + +class YaxisTitle extends ChartTitle implements \JsonSerializable { + + private $text; + + /** + * @return string + */ + public function getText() { + return $this->text; + } + + /** + * @param string $text + */ + public function setText($text) { + $this->text = $text; + } + + /** + * @return array + */ + public function jsonSerialize() { + $vars = get_object_vars($this); + + return $vars; + } + +} diff --git a/src/Form/ChartsConfigForm.php b/src/Form/ChartsConfigForm.php new file mode 100644 index 0000000..3a6667f --- /dev/null +++ b/src/Form/ChartsConfigForm.php @@ -0,0 +1,518 @@ +<?php + +namespace Drupal\charts\Form; + +use Drupal\Core\Extension\ModuleHandler; +use Drupal\Core\Url; +use Drupal\Core\Link; +use Drupal\Core\Form\ConfigFormBase; +use Drupal\Core\Form\FormStateInterface; +use Drupal\Core\Config\ConfigFactory; +use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\Extension\ModuleHandlerInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; + +class ChartsConfigForm extends ConfigFormBase { + protected $moduleHandler; + + public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler) { + parent::__construct($config_factory); + $this->moduleHandler = $module_handler; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static($container->get('config.factory'), $container->get('module_handler')); + } + + public function getFormId() { + return 'charts_form'; + } + + protected function getEditableConfigNames() { + return ['charts.settings']; + } + + public function buildForm(array $form, FormStateInterface $form_state) { + $config = $this->configFactory->getEditable('charts.settings'); + + $parents = array('charts_default_settings'); + $default_config = $config->get('charts_default_settings'); + if ($default_config == NULL) { + $defaults = [] + $this->charts_default_settings(); + } + else { + $defaults = $default_config + $this->charts_default_settings(); + } + + $field_options = array(); + $url = Url::fromRoute('views_ui.add'); + $link = Link::fromTextAndUrl($this->t('create a new view'), $url) + ->toRenderable(); + + // Add help. + $form['help'] = array( + '#type' => 'markup', + '#markup' => '<p>' . $this->t('The settings on this page are used to set + <strong>default</strong> settings. They do not affect existing charts. + To make a new chart, <a href="!views">create a new view</a> and select + the display format of "Chart".', array('!views' => $link['url'])) + . '</p>', + '#weight' => -100, + ); + // Reuse the global settings form for defaults, but remove JS classes. + $form = $this->charts_settings_form($form, $defaults, $field_options, $parents); + $form['xaxis']['#attributes']['class'] = array(); + $form['yaxis']['#attributes']['class'] = array(); + $form['display']['colors']['#prefix'] = NULL; + $form['display']['colors']['#suffix'] = NULL; + // Put settings into vertical tabs. + $form['display']['#group'] = 'defaults'; + $form['xaxis']['#group'] = 'defaults'; + $form['yaxis']['#group'] = 'defaults'; + $form['defaults'] = array('#type' => 'vertical_tabs',); + // Add submit buttons and normal saving behavior. + $form['actions']['#type'] = 'actions'; + $form['actions']['submit'] = array( + '#type' => 'submit', + '#value' => $this->t('Save defaults'), + ); + + return $form; + } + + public function charts_default_settings() { + $defaults = array(); + $defaults['type'] = 'pie'; + $defaults['library'] = NULL; + $defaults['label_field'] = NULL; + $defaults['data_fields'] = NULL; + $defaults['field_colors'] = NULL; + $defaults['title'] = ''; + $defaults['title_position'] = 'out'; + $defaults['legend'] = TRUE; + $defaults['legend_position'] = 'right'; + $defaults['colors'] = $this->charts_default_colors(); + $defaults['background'] = ''; + $defaults['tooltips'] = TRUE; + $defaults['tooltips_use_html'] = FALSE; + $defaults['width'] = NULL; + $defaults['height'] = NULL; + + $defaults['xaxis_title'] = ''; + $defaults['xaxis_labels_rotation'] = 0; + + $defaults['yaxis_title'] = ''; + $defaults['yaxis_min'] = ''; + $defaults['yaxis_max'] = ''; + $defaults['yaxis_prefix'] = ''; + $defaults['yaxis_suffix'] = ''; + $defaults['yaxis_decimal_count'] = ''; + $defaults['yaxis_labels_rotation'] = 0; + + //\Drupal::moduleHandler()->alter('charts_default_settings', $defaults); + $this->moduleHandler->alter('charts_default_settings', $defaults); + + return $defaults; + } + + /** + * Default colors used in all libraries. + */ + public function charts_default_colors() { + return array( + '#2f7ed8', + '#0d233a', + '#8bbc21', + '#910000', + '#1aadce', + '#492970', + '#f28f43', + '#77a1e5', + '#c42525', + '#a6c96a', + ); + } + + public function charts_settings_form($form, $defaults = array(), $field_options = array(), $parents = array()) { + // Ensure all defaults are set. + $options = array_merge($this->charts_default_settings(), $defaults); + + $form['#attached']['library'][] = array('charts', 'charts.admin'); + + // Get a list of available chart libraries. + $charts_info = $this->charts_info(); + $library_options = array(); + foreach ($charts_info as $library_name => $library_info) { + $library_options[$library_name] = $library_info['label']; + } + $form['library'] = array( + '#title' => $this->t('Charting library'), + '#type' => 'select', + '#options' => $library_options, + '#default_value' => $options['library'], + '#required' => TRUE, + '#access' => count($library_options) > 1, + '#attributes' => array('class' => array('chart-library-select')), + '#weight' => -15, + '#parents' => array_merge($parents, array('library')), + ); + + //$chart_types = $this->charts_type_info(); + //This is a work around will need to revisit this + $chart_types = $this->charts_charts_type_info(); + $type_options = array(); + foreach ($chart_types as $chart_type => $chart_type_info) { + $type_options[$chart_type] = $chart_type_info['label']; + } + + $form['type'] = array( + '#title' => $this->t('Chart type'), + '#type' => 'radios', + '#default_value' => $options['type'], + '#options' => $type_options, + '#required' => TRUE, + '#weight' => -20, + '#attributes' => array( + 'class' => array( + 'chart-type-radios', + 'container-inline', + ) + ), + '#parents' => array_merge($parents, array('type')), + ); + + // Set data attributes to identify special properties of different types. + foreach ($chart_types as $chart_type => $chart_type_info) { + if ($chart_type_info['axis_inverted']) { + $form['type'][$chart_type]['#attributes']['data-axis-inverted'] = TRUE; + } + if ($chart_type_info['axis'] === CHARTS_SINGLE_AXIS) { + $form['type'][$chart_type]['#attributes']['data-axis-single'] = TRUE; + } + } + + if ($field_options) { + $first_field = key($field_options); + //$field_keys = array_diff($field_options, array($first_field => NULL)); + $form['fields']['#theme'] = 'charts_settings_fields'; + $form['fields']['label_field'] = array( + '#type' => 'radios', + '#title' => $this->t('Label field'), + '#options' => $field_options + array('' => $this->t('No label field')), + '#default_value' => isset($options['label_field']) ? $options['label_field'] : $first_field, + '#weight' => -10, + '#parents' => array_merge($parents, array('label_field')), + ); + $form['fields']['data_fields'] = array( + '#type' => 'checkboxes', + '#title' => $this->t('Data fields'), + '#options' => $field_options, + '#default_value' => isset($options['data_fields']) ? $options['data_fields'] : array_diff(array_keys($field_options), array($first_field)), + '#weight' => -9, + '#parents' => array_merge($parents, array('data_fields')), + ); + $color_count = 0; + foreach ($field_options as $field_name => $field_label) { + $form['fields']['field_colors'][$field_name] = array( + '#type' => 'textfield', + '#attributes' => array('TYPE' => 'color'), + '#size' => 10, + '#maxlength' => 7, + '#theme_wrappers' => array(), + '#default_value' => !empty($options['field_colors'][$field_name]) ? $options['field_colors'][$field_name] : $options['colors'][$color_count], + '#parents' => array_merge($parents, array( + 'field_colors', + $field_name, + )), + ); + $color_count++; + } + } + + $form['display'] = array( + '#title' => $this->t('Display'), + '#type' => 'details', + '#collapsible' => TRUE, + '#collapsed' => TRUE, + ); + $form['display']['title'] = array( + '#title' => $this->t('Chart title'), + '#type' => 'textfield', + '#default_value' => $options['title'], + '#parents' => array_merge($parents, array('title')), + ); + $form['display']['title_position'] = array( + '#title' => $this->t('Title position'), + '#type' => 'select', + '#options' => array( + '' => $this->t('None'), + 'out' => $this->t('Outside'), + 'in' => $this->t('Inside'), + ), + '#default_value' => $options['title_position'], + '#parents' => array_merge($parents, array('title_position')), + ); + + $form['display']['legend_position'] = array( + '#title' => $this->t('Legend position'), + '#type' => 'select', + '#options' => array( + '' => $this->t('None'), + 'top' => $this->t('Top'), + 'right' => $this->t('Right'), + 'bottom' => $this->t('Bottom'), + 'left' => $this->t('Left'), + ), + '#default_value' => $options['legend_position'], + '#parents' => array_merge($parents, array('legend_position')), + ); + + $form['display']['colors'] = array( + '#title' => $this->t('Chart colors'), + '#theme_wrappers' => array('form_element'), + '#prefix' => '<div class="chart-colors">', + '#suffix' => '</div>', + ); + for ($color_count = 0; $color_count < 10; $color_count++) { + $form['display']['colors'][$color_count] = array( + '#type' => 'textfield', + '#attributes' => array('TYPE' => 'color'), + '#size' => 10, + '#maxlength' => 7, + '#theme_wrappers' => array(), + '#suffix' => ' ', + '#default_value' => $options['colors'][$color_count], + '#parents' => array_merge($parents, array('colors', $color_count)), + ); + } + $form['display']['background'] = array( + '#title' => $this->t('Background color'), + '#type' => 'textfield', + '#size' => 10, + '#maxlength' => 7, + '#attributes' => array('placeholder' => $this->t('transparent')), + '#description' => $this->t('Leave blank for a transparent background.'), + '#default_value' => $options['background'], + '#parents' => array_merge($parents, array('background')), + ); + + $form['display']['dimensions'] = array( + '#title' => $this->t('Dimensions'), + '#theme_wrappers' => array('form_element'), + '#description' => $this->t('If dimensions are left empty, the chart will fill its containing element.'), + ); + $form['display']['dimensions']['width'] = array( + '#type' => 'textfield', + '#attributes' => array( + 'TYPE' => 'number', + 'step' => 1, + 'min' => 0, + 'max' => 9999, + 'placeholder' => $this->t('auto'), + ), + '#default_value' => $options['width'], + '#size' => 8, + '#suffix' => ' x ', + '#theme_wrappers' => array(), + '#parents' => array_merge($parents, array('width')), + ); + $form['display']['dimensions']['height'] = array( + '#type' => 'textfield', + '#attributes' => array( + 'TYPE' => 'number', + 'step' => 1, + 'min' => 0, + 'max' => 9999, + 'placeholder' => $this->t('auto'), + ), + '#default_value' => $options['height'], + '#size' => 8, + '#suffix' => ' px', + '#theme_wrappers' => array(), + '#parents' => array_merge($parents, array('height')), + ); + + $form['xaxis'] = array( + '#title' => $this->t('Horizontal axis'), + '#type' => 'details', + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#attributes' => array('class' => array('chart-xaxis')), + ); + $form['xaxis']['title'] = array( + '#title' => $this->t('Custom title'), + '#type' => 'textfield', + '#default_value' => $options['xaxis_title'], + '#parents' => array_merge($parents, array('xaxis_title')), + ); + $form['xaxis']['labels_rotation'] = array( + '#title' => $this->t('Labels rotation'), + '#type' => 'select', + '#options' => array( + 0 => $this->t('0°'), + 30 => $this->t('30°'), + 45 => $this->t('45°'), + 60 => $this->t('60°'), + 90 => $this->t('90°'), + ), // This is only shown on non-inverted charts. + '#attributes' => array('class' => array('axis-inverted-hide')), + '#default_value' => $options['xaxis_labels_rotation'], + '#parents' => array_merge($parents, array('xaxis_labels_rotation')), + ); + + $form['yaxis'] = array( + '#title' => $this->t('Vertical axis'), + '#type' => 'details', + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#attributes' => array('class' => array('chart-yaxis')), + ); + $form['yaxis']['title'] = array( + '#title' => $this->t('Custom title'), + '#type' => 'textfield', + '#default_value' => $options['yaxis_title'], + '#parents' => array_merge($parents, array('yaxis_title')), + ); + $form['yaxis']['minmax'] = array( + '#title' => $this->t('Value range'), + '#theme_wrappers' => array('form_element'), + ); + $form['yaxis']['minmax']['min'] = array( + '#type' => 'textfield', + '#attributes' => array( + 'TYPE' => 'number', + 'max' => 999999, + 'placeholder' => $this->t('Minimum'), + ), + '#default_value' => $options['yaxis_min'], + '#size' => 12, + '#parents' => array_merge($parents, array('yaxis_min')), + '#suffix' => ' ', + '#theme_wrappers' => array(), + ); + $form['yaxis']['minmax']['max'] = array( + '#type' => 'textfield', + '#attributes' => array( + 'TYPE' => 'number', + 'max' => 999999, + 'placeholder' => $this->t('Maximum'), + ), + '#default_value' => $options['yaxis_max'], + '#size' => 12, + '#parents' => array_merge($parents, array('yaxis_max')), + '#theme_wrappers' => array(), + ); + $form['yaxis']['prefix'] = array( + '#title' => $this->t('Value prefix'), + '#type' => 'textfield', + '#default_value' => $options['yaxis_prefix'], + '#size' => 12, + '#parents' => array_merge($parents, array('yaxis_prefix')), + ); + $form['yaxis']['suffix'] = array( + '#title' => $this->t('Value suffix'), + '#type' => 'textfield', + '#default_value' => $options['yaxis_suffix'], + '#size' => 12, + '#parents' => array_merge($parents, array('yaxis_suffix')), + ); + $form['yaxis']['decimal_count'] = array( + '#title' => $this->t('Decimal count'), + '#type' => 'textfield', + '#attributes' => array( + 'TYPE' => 'number', + 'step' => 1, + 'min' => 0, + 'max' => 20, + 'placeholder' => $this->t('auto'), + ), + '#default_value' => $options['yaxis_decimal_count'], + '#size' => 5, + '#description' => $this->t('Enforce a certain number of decimal-place digits in displayed values.'), + '#parents' => array_merge($parents, array('yaxis_decimal_count')), + ); + $form['yaxis']['labels_rotation'] = array( + '#title' => $this->t('Labels rotation'), + '#type' => 'select', + '#options' => array( + 0 => $this->t('0°'), + 30 => $this->t('30°'), + 45 => $this->t('45°'), + 60 => $this->t('60°'), + 90 => $this->t('90°'), + ), // This is only shown on inverted charts. + '#attributes' => array( + 'class' => array('axis-inverted-show') + ), + '#default_value' => $options['yaxis_labels_rotation'], + '#parents' => array_merge($parents, array('yaxis_labels_rotation')), + ); + + return $form; + } + + public function charts_info() { + $charts_info = array(); + foreach ($this->moduleHandler->getImplementations('charts_info') as $module) { + $module_charts_info = $this->moduleHandler->invoke($module, 'charts_info'); + foreach ($module_charts_info as $chart_library => $chart_library_info) { + $module_charts_info[$chart_library]['module'] = $module; + } + $charts_info = array_merge($charts_info, $module_charts_info); + } + + $this->moduleHandler->alter('charts_info', $charts_info); + + return $charts_info; + } + + /** + * @return mixed + */ + public function charts_charts_type_info() { + $chart_types['pie'] = array( + 'label' => $this->t('Pie'), + 'axis' => CHARTS_SINGLE_AXIS, + ); + $chart_types['bar'] = array( + 'label' => $this->t('Bar'), + 'axis' => CHARTS_DUAL_AXIS, + 'axis_inverted' => TRUE, + 'stacking' => TRUE, + ); + $chart_types['column'] = array( + 'label' => $this->t('Column'), + 'axis' => CHARTS_DUAL_AXIS, + 'stacking' => TRUE, + ); + $chart_types['line'] = array( + 'label' => $this->t('Line'), + 'axis' => CHARTS_DUAL_AXIS, + ); + $chart_types['area'] = array( + 'label' => $this->t('Area'), + 'axis' => CHARTS_DUAL_AXIS, + 'stacking' => TRUE, + ); + $chart_types['scatter'] = array( + 'label' => $this->t('Scatter'), + 'axis' => CHARTS_DUAL_AXIS, + ); + + return $chart_types; + } + + /** + * @param array $form + * @param \Drupal\Core\Form\FormStateInterface $form_state + */ + public function submitForm(array &$form, FormStateInterface $form_state) { + $config = $this->configFactory->getEditable('charts.settings'); + $config->set('charts_default_settings', $form_state->getValue('charts_default_settings')); + $config->save(); + } + +} diff --git a/src/Plugin/views/display/ChartsPluginDisplayChart.php b/src/Plugin/views/display/ChartsPluginDisplayChart.php new file mode 100644 index 0000000..3d3c184 --- /dev/null +++ b/src/Plugin/views/display/ChartsPluginDisplayChart.php @@ -0,0 +1,168 @@ +<?php +/** + * @file + * Contains the Chart display type (similar to Page, Block, Attachment, etc.) + */ + +namespace Drupal\charts\Plugin\views\display; + +use Drupal\Core\Form\FormStateInterface; +use Drupal\views\Plugin\views\display\Attachment; +use Drupal\views\Plugin\views\display\DisplayPluginBase; +use Drupal\views\ViewExecutable; + +/** + * Display plugin to attach multiple chart configurations to the same chart. + * + * @ingroup views_display_plugins + * + * @ViewsDisplay( + * id = "chart_extension", + * title = @Translation("Chart attachment"), + * help = @Translation("Display that produces a chart."), + * theme = "views_view_charts", + * contextual_links_locations = {""} + * ) + * + */ +class ChartsPluginDisplayChart extends Attachment { + + /** + * {@inheritdoc} + */ + protected function defineOptions() { + $options = parent::defineOptions(); + $options['style_plugin']['default'] = 'chart'; + $options['inherit_yaxis'] = array('default' => '1'); + + return $options; + + } + + public function execute() { + return $this->view->render($this->display['id']); + } + + /** + * Provide the summary for page options in the views UI. + * + * This output is returned as an array. + * + * @param $categories + * @param $options + */ + public function optionsSummary(&$categories, &$options) { + // It is very important to call the parent function here: + parent::optionsSummary($categories, $options); + + $categories['attachment'] = [ + 'title' => t('Chart settings'), + 'column' => 'second', + 'build' => ['#weight' => -10,], + ]; + $displays = array_filter($this->getOption('displays')); + if (count($displays) > 1) { + $attach_to = $this->t('Multiple displays'); + } + elseif (count($displays) == 1) { + $display = array_shift($displays); + if ($display = $this->view->storage->getDisplay($display)) { + $attach_to = $display['display_title']; + } + } + if (!isset($attach_to)) { + $attach_to = $this->t('Not defined'); + } + $options['displays'] = array( + 'category' => 'attachment', + 'title' => $this->t('Parent display'), + 'value' => $attach_to, + ); + + $options['inherit_yaxis'] = array( + 'category' => 'attachment', + 'title' => $this->t('Axis settings'), + 'value' => $this->getOption('inherit_yaxis') ? t('Use primary Y-axis') : t('Create secondary axis'), + ); + + $options['attachment_position'] = array('disabled' => TRUE); + + $options['inherit_pager'] = array('disabled' => TRUE); + + $options['render_pager'] = array('disabled' => TRUE); + + } + + /** + * Provide the default form for setting options. + * + * @param $form + * @param FormStateInterface $form_state + */ + public function buildOptionsForm(&$form, FormStateInterface $form_state) { + parent::buildOptionsForm($form, $form_state); + + switch ($form_state->get('section')) { + case 'displays': + $form['#title'] .= t('Parent display'); + break; + case 'inherit_yaxis': + $form['#title'] .= t('Axis settings'); + $form['inherit_yaxis'] = array( + '#title' => t('Y-Axis settings'), + '#type' => 'radios', + '#options' => array( + 1 => t('Inherit primary of parent display'), + 0 => t('Create a secondary axis'), + ), + '#default_value' => $this->getOption('inherit_yaxis'), + '#description' => t('In most charts, the X and Y axis from the parent display are both shared with each attached child chart. However, if this chart is going to use a different unit of measurement, a secondary axis may be added on the opposite side of the normal Y-axis.'), + ); + break; + } + + + } + + /** + * Perform any necessary changes to the form values prior to storage. + * There is no need for this function to actually store the data. + * + * @param $form + * @param FormStateInterface $form_state + */ + public function submitOptionsForm(&$form, FormStateInterface $form_state) { + // It is very important to call the parent function here: + parent::submitOptionsForm($form, $form_state); + $section = $form_state->get('section'); + switch ($section) { + case 'displays': + $form_state->setValue($section, array_filter($form_state->getValue($section))); + break; + case 'inherit_arguments': + case 'inherit_exposed_filters': + case 'inherit_yaxis': + $this->setOption($section, $form_state->getValue($section)); + break; + } + + } + + /** + * {@inheritdoc} + */ + public function attachTo(ViewExecutable $view, $display_id, array &$build) { + + $displays = $this->getOption('displays'); + if (empty($displays[$display_id])) { + return; + } + + if (!$this->access()) { + return; + } + + } + +} + diff --git a/views/charts_plugin_style_chart.inc b/src/Plugin/views/style/ChartsPluginStyleChart.php similarity index 58% rename from views/charts_plugin_style_chart.inc rename to src/Plugin/views/style/ChartsPluginStyleChart.php index f991d15..2f40b71 100644 --- a/views/charts_plugin_style_chart.inc +++ b/src/Plugin/views/style/ChartsPluginStyleChart.php @@ -1,24 +1,42 @@ <?php -/** - * @file - * Contains the Chart style (format) plugin (similar to Table, HTML List, etc.) - */ + +namespace Drupal\charts\Plugin\views\style; + +use Drupal\core\form\FormStateInterface; +use Drupal\Core\Render\Element; +use Drupal\views\Plugin\views\style\StylePluginBase; + +\Drupal::moduleHandler()->loadInclude('charts', 'inc', 'charts.theme'); +\Drupal::moduleHandler()->loadInclude('charts', 'inc', 'includes/charts.pages'); /** * Style plugin to render view as a chart. * * @ingroup views_style_plugins + * + * @ViewsStyle( + * id = "chart", + * title = @Translation("Chart"), + * help = @Translation("Render a chart of your data."), + * theme = "views_view_charts", + * display_types = { "normal" } + * ) */ -class charts_plugin_style_chart extends views_plugin_style { +class ChartsPluginStyleChart extends StylePluginBase { + + protected $usesGrouping = FALSE; + protected $usesFields = TRUE; + protected $usesRowPlugin = TRUE; + /** * Set default options. */ - function option_definition() { - $options = parent::option_definition(); + protected function defineOptions() { + $options = parent::defineOptions(); + + // Get the default chart values. + $defaults = \Drupal::state()->get('charts_default_settings', array()); - // Get the default chart values - module_load_include('inc', 'charts', 'includes/charts.pages'); - $defaults = variable_get('charts_default_settings', array()); $defaults += charts_default_settings(); foreach ($defaults as $default_key => $default_value) { $options[$default_key]['default'] = $default_value; @@ -26,24 +44,23 @@ class charts_plugin_style_chart extends views_plugin_style { // Remove the default setting for chart type so it can be inherited if this // is a chart extension type. - if ($this->plugin_name === 'chart_extension') { + if ($this->view->style_plugin === 'chart_extension') { $options['type']['default'] = NULL; } + $options['path'] = array('default' => 'charts'); return $options; } /** - * Generate a form for setting options. + * {@inheritdoc} */ - function options_form(&$form, &$form_state) { - parent::options_form($form, $form_state); - $handlers = $this->display->handler->get_handlers('field'); + public function buildOptionsForm(&$form, FormStateInterface $form_state) { + parent::buildOptionsForm($form, $form_state); + + $handlers = $this->displayHandler->getHandlers('field'); if (empty($handlers)) { - $form['error_markup'] = array( - '#markup' => '<div class="error messages">' . t('You need at least one field before you can configure your table settings') . '</div>', - ); - return; + $form['error_markup'] = array('#markup' => '<div class="error messages">' . t('You need at least one field before you can configure your table settings') . '</div>',); } // Limit grouping options (we only support one grouping field). @@ -60,80 +77,78 @@ class charts_plugin_style_chart extends views_plugin_style { } // Merge in the global chart settings form. - module_load_include('inc', 'charts', 'includes/charts.pages'); - $field_options = $this->display->handler->get_field_labels(); + $field_options = $this->displayHandler->getFieldLabels(); $form = charts_settings_form($form, $this->options, $field_options, array('style_options')); // Reduce the options if this is a chart extension. - if ($parent_display = $this->get_parent_chart_display()) { - $parent_chart_type = chart_get_type($parent_display->display_options['style_options']['type']); - if (empty($form['type']['#default_value'])) { - $form['type']['#default_value'] = $parent_display->display_options['style_options']['type']; - } - + /*if (empty($this->displayHandler->getAttachedDisplays())) { $form['type']['#description'] = empty($form['type']['#description']) ? '' : $form['type']['#description'] . ' '; - $form['type']['#description'] .= t('This chart will be combined with the parent display "@display_title", which is a "@type" chart. Not all chart types may be combined. Selecting a different chart type than the parent may cause errors.', array('@display_title' => $parent_display->display_title, '@type' => $parent_chart_type['label'])); + $form['type']['#description'] .= t('This chart will be combined with the parent display "@display_title", + which is a "@type" chart. Not all chart types may be combined. Selecting a different chart type than + the parent may cause errors.' //, + // array('@display_title' => $parent_display->display_title, '@type' => $parent_chart_type['label']) + ); $form['fields']['label_field']['#disabled'] = TRUE; $form['display']['#access'] = FALSE; $form['xaxis']['#access'] = FALSE; - if ($this->display->handler->get_option('inherit_yaxis')) { + if ($this->displayHandler->options['inherit_yaxis']) { $form['yaxis']['#access'] = FALSE; } else { $form['yaxis']['#title'] = t('Secondary axis'); $form['yaxis']['#attributes']['class'] = array(); } - } + }*/ } /** - * Generate a form for setting options. + * {@inheritdoc} */ - function options_submit(&$form, &$form_state) { - parent::options_submit($form, $form_state); + public function submitOptionsForm(&$form, FormStateInterface $form_state) { + parent::submitOptionsForm($form, $form_state); } + /** - * Make sure the display and all associated handlers are valid. - * - * @return - * Empty array if the display is valid; an array of error strings if it is not. + * {@inheritdoc} */ - function validate() { - $errors = array(); - $field_handlers = $this->display->handler->get_handlers('field'); - - // Don't execute validation on the new view page. - if ($_GET['q'] === 'admin/structure/views/add') { - return; - } - - // Make sure that all chart extensions first have a parent chart selected. - if ($this->plugin_name === 'chart_extension' && $this->get_parent_chart_display() === FALSE) { - $errors[] = t('This chart add-on must have a parent chart selected under the chart settings.'); - } - // Make sure that at least one data column has been selected. - elseif (count($field_handlers)) { - $data_field_key = !empty($this->options['data_fields']) ? current($this->options['data_fields']) : NULL; - if (empty($data_field_key)) { - $errors[] = t('At least one data field must be selected in the chart configuration before this chart may be shown'); - } - else { - $data_field = isset($field_handlers[$data_field_key]) ? $field_handlers[$data_field_key] : NULL; - if (!isset($data_field)) { - $errors[] = t('A field you have specified as a data field in your chart settings no longer exists. Edit the chart settings and select at least one data field.'); + public function validate() { + + $errors = parent::validate(); + $dataFields = $this->options['data_fields']; + $dataFieldsValueState = array(); + $dataFieldsCounter = 0; + + foreach ($dataFields as $value) { + /*if (count(array_unique($plugin)) === 1 && end($plugin) === 0) { + $errors[] = $this->t('At least one data field must be selected in the chart configuration before this chart may be shown'); + }*/ + /*Skip title field no need to validate it and if data field is set add to dataFieldsValueState array state 1 + otherwise add to same array state 0*/ + if ($dataFieldsCounter > 0) { + if (empty($value)) { + array_push($dataFieldsValueState, 0); + } else { + array_push($dataFieldsValueState, 1); } } + $dataFieldsCounter++; + } + /*If total sum of dataFieldsValueState is less than 1, then no dataFields were selected otherwise 1 or more selected + total sum will be greater than 1*/ + if (array_sum($dataFieldsValueState) < 1) { + $errors[] = $this->t('At least one data field must be selected in the chart configuration before this chart may be shown'); } return $errors; } /** - * Render the entire view from the view result. + * {@inheritdoc} */ - function render() { - $field_handlers = $this->display->handler->get_handlers('field'); + public function render() { + + $field_handlers = $this->view->getHandlers('field'); // Calculate the labels field alias. $label_field = FALSE; @@ -155,14 +170,13 @@ class charts_plugin_style_chart extends views_plugin_style { if (isset($data_fields[$label_field_key])) { unset($data_fields[$label_field_key]); } - - $chart_id = $this->view->name . '__' . $this->view->current_display; + $chart_id = $this->view->id() . '__' . $this->view->current_display; $chart = array( '#type' => 'chart', '#chart_type' => $this->options['type'], '#chart_library' => $this->options['library'], '#chart_id' => $chart_id, - '#id' => drupal_clean_css_identifier('chart_' . $chart_id), + '#id' => ('chart_' . $chart_id), '#title' => $this->options['title_position'] ? $this->options['title'] : FALSE, '#title_position' => $this->options['title_position'], '#tooltips' => $this->options['tooltips'], @@ -173,30 +187,31 @@ class charts_plugin_style_chart extends views_plugin_style { '#legend_position' => $this->options['legend_position'] ? $this->options['legend_position'] : NULL, '#width' => $this->options['width'], '#height' => $this->options['height'], - '#view' => $this->view, // pass info about the actual view results to allow further processing + '#view' => $this->view, + // Pass info about the actual view results to allow further processing + '#theme' => 'views_view_charts', ); - $chart_type_info = chart_get_type($this->options['type']); - + $chart_type_info = charts_get_type($this->options['type']); if ($chart_type_info['axis'] === CHARTS_SINGLE_AXIS) { $data_field_key = key($data_fields); $data_field = $data_fields[$data_field_key]; $data = array(); - $renders = $this->render_fields($this->view->result); + $this->renderFields($this->view->result); + $renders = $this->rendered_fields; foreach ($renders as $row_number => $row) { $data_row = array(); if ($label_field_key) { // Labels need to be decoded, as the charting library will re-encode. - $data_row[] = htmlspecialchars_decode($this->get_field($row_number, $label_field_key), ENT_QUOTES); + $data_row[] = htmlspecialchars_decode($this->getField($row_number, $label_field_key), ENT_QUOTES); } - $value = $this->get_field($row_number, $data_field_key); + $value = $this->getField($row_number, $data_field_key); // Convert empty strings to NULL. if ($value === '') { $value = NULL; - } - // Strip thousands placeholders if present, then cast to float. + } // Strip thousands placeholders if present, then cast to float. else { - $value = (float) str_replace(array(',', ' '), '', $value); + $value = (float)str_replace(array(',', ' '), '', $value); } $data_row[] = $value; $data[] = $data_row; @@ -211,8 +226,8 @@ class charts_plugin_style_chart extends views_plugin_style { '#data' => $data, '#title' => $data_field->options['label'], ); - } - else { + + } else { $chart['xaxis'] = array( '#type' => 'chart_xaxis', '#title' => $this->options['xaxis_title'] ? $this->options['xaxis_title'] : FALSE, @@ -226,7 +241,8 @@ class charts_plugin_style_chart extends views_plugin_style { '#min' => $this->options['yaxis_min'], ); - $sets = $this->render_grouping($this->view->result, $this->options['grouping'], TRUE); + // @todo incorporate this patch: https://www.drupal.org/files/issues/charts_grouping-2146927-6.patch. + $sets = $this->renderGrouping($this->view->result, $this->options['grouping'], TRUE); foreach ($sets as $series_label => $data_set) { $series_index = isset($series_index) ? $series_index + 1 : 0; $series_key = $this->view->current_display . '__' . $field_key . '_' . $series_index; @@ -249,7 +265,7 @@ class charts_plugin_style_chart extends views_plugin_style { $row_number = 0; foreach ($data_set['rows'] as $result_number => $row) { if ($label_field_key && !isset($chart['xaxis']['#labels'][$row_number])) { - $chart['xaxis']['#labels'][$row_number] = $this->get_field($result_number, $label_field_key); + $chart['xaxis']['#labels'][$row_number] = $this->getField($result_number, $label_field_key); } foreach ($data_fields as $field_key => $field_handler) { // Don't allow the grouping field to provide data. @@ -257,14 +273,13 @@ class charts_plugin_style_chart extends views_plugin_style { continue; } - $value = $this->get_field($result_number, $field_key); + $value = $this->getField($result_number, $field_key); // Convert empty strings to NULL. if ($value === '') { $value = NULL; - } - // Strip thousands placeholders if present, then cast to float. + } // Strip thousands placeholders if present, then cast to float. else { - $value = (float) str_replace(array(',', ' '), '', $value); + $value = (float)str_replace(array(',', ' '), '', $value); } $chart[$series_key]['#data'][] = $value; } @@ -275,32 +290,33 @@ class charts_plugin_style_chart extends views_plugin_style { // Check if this display has any children charts that should be applied // on top of it. - $parent_display_id = $this->view->current_display; - $children_displays = $this->get_children_chart_displays(); - foreach ($children_displays as $child_display_id => $child_display) { + $children_displays = $this->getChildrenChartDisplays(); + //contains the different subviews of the attachments + $attachments = array(); + $service = \Drupal::service('charts.charts_attachment'); + + foreach ($children_displays as $child_display) { // If the user doesn't have access to the child display, skip. - if (!$this->view->access($child_display_id)) { + if (!$this->view->access($child_display)) { continue; } // Generate the subchart by executing the child display. We load a fresh // view here to avoid collisions in shifting the current display while in // a display. - $subview = $this->view->clone_view(); - $subview->set_display($child_display_id); - - // Copy the settings for our axes over to the child view. + $subview = $this->view->createDuplicate(); + $subview->setDisplay($child_display); + // Copy the settings for our axes over to the child view. foreach ($this->options as $option_name => $option_value) { - if (strpos($option_name, 'yaxis') === 0 && $child_display->handler->get_option('inherit_yaxis')) { + if (strpos($option_name, 'yaxis') === 0 && $this->view->storage->getDisplay($child_display)['display_options']['inherit_yaxis']) { $subview->display_handler->options['style_options'][$option_name] = $option_value; - } - elseif (strpos($option_name, 'xaxis') === 0) { + } elseif (strpos($option_name, 'xaxis') === 0) { $subview->display_handler->options['style_options'][$option_name] = $option_value; } } // Execute the subview and get the result. - $subview->pre_execute(); + $subview->preExecute(); $subview->execute(); // If there's no results, don't attach the subview. @@ -308,18 +324,21 @@ class charts_plugin_style_chart extends views_plugin_style { continue; } - $subchart = $subview->style_plugin->render($subview->result); - $subview->post_execute(); - unset($subview); + $subchart = $subview->style_plugin->render(); + array_push($attachments, $subview); //add attachment views to attachments array + /*$subview->postExecute(); + unset($subview);*/ - // Create a secondary axis if needed. - if (!$child_display->handler->get_option('inherit_yaxis') && isset($subchart['yaxis'])) { + // Create a secondary axis if needed. + if ($this->view->storage->getDisplay($child_display)['display_options']['inherit_yaxis'] !== '1' && isset($subchart['yaxis'])) { $chart['secondary_yaxis'] = $subchart['yaxis']; $chart['secondary_yaxis']['#opposite'] = TRUE; } // Merge in the child chart data. - foreach (element_children($subchart) as $key) { + //foreach (\Drupal::state()->getMultiple($subchart) as $key) { + //foreach (\Drupal::state()->getMultiple($subchart) as $key) { + foreach (Element::children($subchart) as $key) { if ($subchart[$key]['#type'] === 'chart_data') { $chart[$key] = $subchart[$key]; // If the subchart is a different type than the parent chart, set @@ -327,12 +346,13 @@ class charts_plugin_style_chart extends views_plugin_style { if ($subchart['#chart_type'] !== $chart['#chart_type']) { $chart[$key]['#chart_type'] = $subchart['#chart_type']; } - if (!$child_display->handler->get_option('inherit_yaxis')) { + if ($this->view->storage->getDisplay($child_display)['display_options']['inherit_yaxis'] !== '1') { $chart[$key]['#target_axis'] = 'secondary_yaxis'; } } } } + $service->setAttachmentViews($attachments); // Print the chart. return $chart; @@ -343,33 +363,18 @@ class charts_plugin_style_chart extends views_plugin_style { */ function get_parent_chart_display() { $parent_display = FALSE; - if ($this->plugin_name === 'chart_extension' && $this->display && $this->display->handler->options['parent_display']) { - $parent_display_name = $this->display->handler->options['parent_display']; - if (isset($this->view->display[$parent_display_name])) { - $parent_display = $this->view->display[$parent_display_name]; - } - } - // Ensure the parent is a chart. - if ($parent_display && $parent_display->display_options['style_plugin'] !== 'chart') { - $parent_display = FALSE; - } + return $parent_display; } /** * Utility function to check if this chart has children displays. */ - function get_children_chart_displays() { - $children_displays = array(); - foreach ($this->view->display as $display_name => $display) { - $display_enabled = $this->view->display[$display_name]->handler->get_option('enabled'); - if ($display->display_plugin === 'chart' && $display->display_options['parent_display'] && empty($display->deleted) && $display_enabled) { - $parent_display_name = $display->display_options['parent_display']; - if ($parent_display_name === $this->view->current_display) { - $children_displays[$display_name] = $this->view->display[$display_name]; - } - } - } + function getChildrenChartDisplays() { + + $children_displays = $this->displayHandler->getAttachedDisplays(); + return $children_displays; } + } diff --git a/src/Services/ChartAttachmentService.php b/src/Services/ChartAttachmentService.php new file mode 100644 index 0000000..c2a0855 --- /dev/null +++ b/src/Services/ChartAttachmentService.php @@ -0,0 +1,30 @@ +<?php + +namespace Drupal\charts\Services; + +/** + * Class ChartAttachmentService + * + * @package Drupal\charts\Services. + */ + +class ChartAttachmentService implements ChartAttachmentServiceInterface { + + private $attachmentViews; + + /** + * @return $attachmentViews an array of different views. + */ + public function getAttachmentViews() { + + return $this->attachmentViews; + } + + /** + * @param $attachmentViews is an array of views. + */ + public function setAttachmentViews($attachmentViews) { + $this->attachmentViews = $attachmentViews; + } + +} diff --git a/src/Services/ChartAttachmentServiceInterface.php b/src/Services/ChartAttachmentServiceInterface.php new file mode 100644 index 0000000..c8718c5 --- /dev/null +++ b/src/Services/ChartAttachmentServiceInterface.php @@ -0,0 +1,18 @@ +<?php + +namespace Drupal\charts\Services; + +interface ChartAttachmentServiceInterface { + /** + * @return $attachmentViews an array of different views. + */ + + public function getAttachmentViews(); + + /** + * @param $attachmentViews is an array of views. + */ + + public function setAttachmentViews($attachmentViews); + +} diff --git a/src/Services/ChartService.php b/src/Services/ChartService.php new file mode 100644 index 0000000..3483a48 --- /dev/null +++ b/src/Services/ChartService.php @@ -0,0 +1,36 @@ +<?php + +namespace Drupal\charts\Services; + +/** + * Service class necessary for getting and setting the state of the currently + * selected Library. + * + * Class ChartService + * + * @package Drupal\charts\Services + */ + +class ChartService implements ChartServiceInterface { + + private $librarySelected; + + /** + * Gets the currently selected Library. + * + * @return string $librarySelected. + */ + public function getLibrarySelected() { + return $this->librarySelected; + } + + /** + * Sets the previously set Library with the newly selected library value. + * + * @param String $librarySelected . + */ + public function setLibrarySelected($librarySelected) { + $this->librarySelected = $librarySelected; + } + +} diff --git a/src/Services/ChartServiceInterface.php b/src/Services/ChartServiceInterface.php new file mode 100644 index 0000000..cef0129 --- /dev/null +++ b/src/Services/ChartServiceInterface.php @@ -0,0 +1,23 @@ +<?php + +namespace Drupal\charts\Services; + +/** + * Interface ChartServiceInterface. + * + * @package Drupal\charts\Services + */ + +interface ChartServiceInterface { + /** + * @return mixed + */ + + public function getLibrarySelected(); + + /** + * @param $librarySelected + */ + public function setLibrarySelected($librarySelected); + +} diff --git a/src/Util/Util.php b/src/Util/Util.php new file mode 100644 index 0000000..f190d0b --- /dev/null +++ b/src/Util/Util.php @@ -0,0 +1,77 @@ +<?php + +namespace Drupal\charts\Util; + +class Util { + + /** + * @param $view + * @param $labelValues + * @param $labelField + * @param $color + * @return array + */ + public static function viewsData($view, $labelValues, $labelField, $color) { + $data = array(); + + foreach ($view->result as $id => $row) { + $numberFields = 0; + $rowData = array(); + foreach ($labelValues as $fieldId => $rowDataValue) { + $rowData[$numberFields] = array( + 'value' => $view->field[$fieldId]->getValue($row), + 'label_field' => $view->field[$labelField]->getValue($row), + 'label' => $view->field[$fieldId]->label(), + // 'label' => $view->display_handler->display['id'], to use display_id + 'color' => $color[$fieldId], + ); + $numberFields++; + } + $data[$id] = $rowData; + } + + return $data; + } + + /** + * Removes unselected fields + */ + + public static function removeUnselectedFields($valueField) { + $fieldValues = array(); + foreach ($valueField as $key => $value) { + if (!empty($value)) { + $fieldValues[$key] = $value; + } + } + return $fieldValues; + } + + /** + * Creates chart data to be used later by visualization frameworks + */ + + public static function createChartableData($data) { + $chartData = array(); + $categories = array(); + $seriesData = array(); + + for ($i = 0; $i < count($data[0]); $i++) { + + $seriesRowData = array('name' => '', 'color' => '', 'data' => array()); + for ($j = 0; $j < count($data); $j++) { + $categories[$j] = $data[$j][$i]['label_field']; + $seriesRowData['name'] = $data[$j][$i]['label']; + // $seriesRowData['name'] = $data[$j][$i]['label_field']; + $seriesRowData['color'] = $data[$j][$i]['color']; + array_push($seriesRowData['data'], ((int) ($data[$j][$i]['value']))); + } + array_push($seriesData, $seriesRowData); + } + $chartData[0] = $categories; + $chartData[1] = $seriesData; + + return $chartData; + } + +} diff --git a/templates/views-view-charts.html.twig b/templates/views-view-charts.html.twig new file mode 100644 index 0000000..f740559 --- /dev/null +++ b/templates/views-view-charts.html.twig @@ -0,0 +1,15 @@ +{% if chart_type == 'google' %} + {{ attach_library('charts_google/google') }} + {{ attach_library('charts_google/charts_google') }} +{% elseif chart_type == 'highcharts' %} + {{ attach_library('charts_highcharts/highcharts') }} + {{ attach_library('charts_highcharts/charts_highcharts') }} +{% elseif chart_type == 'c3' %} + {{ attach_library('charts_c3/d3') }} + {{ attach_library('charts_c3/c3') }} + {{ attach_library('charts_c3/charts_c3') }} +{% else %} + Unconfigured charts +{% endif %} +<div {{ attributes }} {{ content_attributes }} id="chart" + style="width:100%; height:400px;"></div> diff --git a/views/charts.views.inc b/views/charts.views.inc deleted file mode 100644 index 2969f57..0000000 --- a/views/charts.views.inc +++ /dev/null @@ -1,44 +0,0 @@ -<?php -/** - * @file - * Views integration with Charts. - */ - -/** - * Implementation of hook_views_plugins(). - * - * Define charts style for Views. - */ -function charts_views_plugins() { - $plugins = array(); - - $plugins['style']['chart'] = array( - 'title' => t('Charts'), - 'help' => t('Displays the content in several Chart styles.'), - 'handler' => 'charts_plugin_style_chart', - 'uses row plugin' => FALSE, - 'uses row class' => FALSE, - 'uses fields' => TRUE, - 'uses options' => TRUE, - 'uses grouping' => TRUE, - 'type' => 'normal', - ); - $plugins['style']['chart_extension'] = $plugins['style']['chart']; - $plugins['style']['chart_extension']['type'] = 'chart'; - - $plugins['display']['chart'] = array( - 'title' => t('Chart add-on'), - 'admin' => t('Chart add-on'), - 'help' => t('Add chart data to a new or existing chart.'), - 'handler' => 'charts_plugin_display_chart', - 'theme' => 'views_view', - 'register theme' => FALSE, - 'use ajax' => FALSE, - 'use pager' => FALSE, - 'use more' => FALSE, - 'accept attachments' => FALSE, - 'type' => 'chart', - ); - - return $plugins; -} diff --git a/views/charts.views_default.inc b/views/charts.views_default.inc deleted file mode 100644 index 5b7f555..0000000 --- a/views/charts.views_default.inc +++ /dev/null @@ -1,163 +0,0 @@ -<?php -/** - * @file - * Default views for Charts module. - */ - -/** - * Implements hook_views_default_views(). - */ -function charts_views_default_views() { - $view = new view(); - $view->name = 'charts_demo'; - $view->description = ''; - $view->tag = 'default'; - $view->base_table = 'node'; - $view->human_name = 'charts_demo'; - $view->core = 7; - $view->api_version = '3.0'; - $view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */ - - /* Display: Master */ - $handler = $view->new_display('default', 'Master', 'default'); - $handler->display->display_options['title'] = 'Chart Examples'; - $handler->display->display_options['use_more_always'] = FALSE; - $handler->display->display_options['group_by'] = TRUE; - $handler->display->display_options['access']['type'] = 'perm'; - $handler->display->display_options['access']['perm'] = 'access example charts'; - $handler->display->display_options['cache']['type'] = 'none'; - $handler->display->display_options['query']['type'] = 'views_query'; - $handler->display->display_options['exposed_form']['type'] = 'basic'; - $handler->display->display_options['pager']['type'] = 'full'; - $handler->display->display_options['pager']['options']['items_per_page'] = '10'; - $handler->display->display_options['style_plugin'] = 'table'; - $handler->display->display_options['style_options']['columns'] = array( - 'type_1' => 'type_1', - 'type' => 'type', - ); - $handler->display->display_options['style_options']['default'] = '-1'; - $handler->display->display_options['style_options']['info'] = array( - 'type_1' => array( - 'sortable' => 0, - 'default_sort_order' => 'asc', - 'align' => '', - 'separator' => '', - 'empty_column' => 0, - ), - 'type' => array( - 'sortable' => 0, - 'default_sort_order' => 'asc', - 'align' => '', - 'separator' => '', - 'empty_column' => 0, - ), - ); - /* Field: Content: Type */ - $handler->display->display_options['fields']['type_1']['id'] = 'type_1'; - $handler->display->display_options['fields']['type_1']['table'] = 'node'; - $handler->display->display_options['fields']['type_1']['field'] = 'type'; - /* Field: COUNT(Content: Type) */ - $handler->display->display_options['fields']['type']['id'] = 'type'; - $handler->display->display_options['fields']['type']['table'] = 'node'; - $handler->display->display_options['fields']['type']['field'] = 'type'; - $handler->display->display_options['fields']['type']['group_type'] = 'count'; - $handler->display->display_options['fields']['type']['label'] = 'Nodes'; - /* Filter criterion: Content: Published */ - $handler->display->display_options['filters']['status']['id'] = 'status'; - $handler->display->display_options['filters']['status']['table'] = 'node'; - $handler->display->display_options['filters']['status']['field'] = 'status'; - $handler->display->display_options['filters']['status']['value'] = 1; - $handler->display->display_options['filters']['status']['group'] = 1; - $handler->display->display_options['filters']['status']['expose']['operator'] = FALSE; - - /* Display: Page */ - $handler = $view->new_display('page', 'Page', 'page'); - $handler->display->display_options['defaults']['header'] = FALSE; - /* Header: Global: Text area */ - $handler->display->display_options['header']['area']['id'] = 'area'; - $handler->display->display_options['header']['area']['table'] = 'views'; - $handler->display->display_options['header']['area']['field'] = 'area'; - $handler->display->display_options['header']['area']['content'] = 'These charts are created using Views. This example may be edited through the Views UI.'; - $handler->display->display_options['header']['area']['format'] = '2'; - $handler->display->display_options['path'] = 'charts/examples/views'; - $handler->display->display_options['menu']['type'] = 'tab'; - $handler->display->display_options['menu']['title'] = 'Views example'; - $handler->display->display_options['menu']['weight'] = '1'; - $handler->display->display_options['menu']['context'] = 0; - $handler->display->display_options['menu']['context_only_inline'] = 0; - - /* Display: Column Chart */ - $handler = $view->new_display('attachment', 'Column Chart', 'column'); - $handler->display->display_options['defaults']['title'] = FALSE; - $handler->display->display_options['title'] = 'Pie Chart'; - $handler->display->display_options['pager']['type'] = 'some'; - $handler->display->display_options['defaults']['style_plugin'] = FALSE; - $handler->display->display_options['style_plugin'] = 'chart'; - $handler->display->display_options['style_options']['library'] = NULL; - $handler->display->display_options['style_options']['type'] = 'column'; - $handler->display->display_options['style_options']['title'] = 'Title text'; - $handler->display->display_options['style_options']['title_position'] = ''; - $handler->display->display_options['style_options']['xaxis_title'] = 'Node type'; - $handler->display->display_options['style_options']['xaxis_labels_rotation'] = '30'; - $handler->display->display_options['style_options']['yaxis_title'] = 'Number of nodes'; - $handler->display->display_options['style_options']['yaxis_min'] = '0'; - $handler->display->display_options['style_options']['yaxis_max'] = '260'; - $handler->display->display_options['style_options']['yaxis_prefix'] = '$'; - $handler->display->display_options['style_options']['yaxis_suffix'] = 'lb'; - $handler->display->display_options['style_options']['yaxis_decimal_count'] = '2'; - $handler->display->display_options['style_options']['yaxis_labels_rotation'] = '30'; - $handler->display->display_options['style_options']['label_field'] = 'type_1'; - $handler->display->display_options['style_options']['data_fields'] = array('type' => 'type'); - $handler->display->display_options['defaults']['style_options'] = FALSE; - $handler->display->display_options['defaults']['row_plugin'] = FALSE; - $handler->display->display_options['defaults']['row_options'] = FALSE; - $handler->display->display_options['displays'] = array( - 'page' => 'page', - 'default' => 0, - 'page_1' => 0, - ); - - /* Display: Pie Chart */ - $handler = $view->new_display('attachment', 'Pie Chart', 'pie'); - $handler->display->display_options['defaults']['title'] = FALSE; - $handler->display->display_options['title'] = 'Pie Chart'; - $handler->display->display_options['pager']['type'] = 'some'; - $handler->display->display_options['defaults']['style_plugin'] = FALSE; - $handler->display->display_options['style_plugin'] = 'chart'; - $handler->display->display_options['style_options']['type'] = 'pie'; - $handler->display->display_options['style_options']['label_field'] = 'type_1'; - $handler->display->display_options['style_options']['data_fields'] = array('type' => 'type'); - $handler->display->display_options['defaults']['style_options'] = FALSE; - $handler->display->display_options['defaults']['row_plugin'] = FALSE; - $handler->display->display_options['defaults']['row_options'] = FALSE; - $handler->display->display_options['displays'] = array( - 'page' => 'page', - 'default' => 0, - ); - $translatables['charts_demo'] = array( - t('Master'), - t('Chart Examples'), - t('more'), - t('Apply'), - t('Reset'), - t('Sort by'), - t('Asc'), - t('Desc'), - t('Items per page'), - t('- All -'), - t('Offset'), - t('« first'), - t('‹ previous'), - t('next ›'), - t('last »'), - t('Type'), - t('Nodes'), - t('Page'), - t('These charts are created using Views. This example may be edited through the Views UI.'), - t('Column Chart'), - t('Pie Chart'), - ); - $views[] = $view; - - return $views; -} diff --git a/views/charts_plugin_display_chart.inc b/views/charts_plugin_display_chart.inc deleted file mode 100644 index eedba28..0000000 --- a/views/charts_plugin_display_chart.inc +++ /dev/null @@ -1,130 +0,0 @@ -<?php -/** - * @file - * Contains the Chart display type (similar to Page, Block, Attachment, etc.) - */ - -/** - * Display plugin to attach multiple chart configurations to the same chart. - * - * @ingroup views_style_plugins - */ -class charts_plugin_display_chart extends views_plugin_display { - function get_style_type() { - return 'chart'; - } - - function option_definition() { - $options = parent::option_definition(); - - // Overrides of standard options. - $options['style_plugin']['default'] = 'chart_extension'; - $options['row_plugin']['default'] = 'fields'; - $options['defaults']['default']['style_plugin'] = FALSE; - $options['defaults']['default']['style_options'] = FALSE; - $options['defaults']['default']['row_plugin'] = FALSE; - $options['defaults']['default']['row_options'] = FALSE; - - $options['parent_display'] = array('default' => ''); - $options['inherit_yaxis'] = array('default' => '1'); - - return $options; - } - - /** - * Provide the summary for page options in the views UI. - * - * This output is returned as an array. - */ - function options_summary(&$categories, &$options) { - // It is very important to call the parent function here: - parent::options_summary($categories, $options); - - $categories['chart'] = array( - 'title' => t('Chart settings'), - 'column' => 'second', - 'build' => array( - '#weight' => -10, - ), - ); - - $parent_title = NULL; - $parent_display = $this->get_option('parent_display'); - if (!empty($this->view->display[$parent_display])) { - $parent_title = check_plain($this->view->display[$parent_display]->display_title); - } - $options['parent_display'] = array( - 'category' => 'chart', - 'title' => t('Combine with parent chart'), - 'value' => $parent_title ? $parent_title : t('None'), - ); - $options['inherit_yaxis'] = array( - 'category' => 'chart', - 'title' => t('Axis settings'), - 'value' => $this->get_option('inherit_yaxis') ? t('Use primary Y-axis') : t('Create secondary axis'), - ); - - } - - /** - * Provide the default form for setting options. - */ - function options_form(&$form, &$form_state) { - // It is very important to call the parent function here: - parent::options_form($form, $form_state); - - switch ($form_state['section']) { - case 'parent_display': - $form['#title'] .= t('Parent display'); - - // Filter down the list of displays to include only those that use - // the chart display style. - $display_options = array(); - foreach ($this->view->display as $display_name => $display) { - if ($display->handler->get_option('style_plugin') === 'chart' && $display_name !== $this->view->current_display) { - $display_options[$display_name] = $display->display_title; - } - } - $form['parent_display'] = array( - '#title' => t('Parent display'), - '#type' => 'select', - '#options' => $display_options, - '#empty_option' => t('- None - '), - '#required' => TRUE, - '#default_value' => $this->get_option('parent_display'), - '#description' => t('Select a parent display onto which this chart will be overlaid. Only other displays using a "Chart" format are included here. This option may be used to create charts with several series of data or to create combination charts.'), - ); - break; - case 'inherit_yaxis': - $form['#title'] .= t('Axis settings'); - $form['inherit_yaxis'] = array( - '#title' => t('Y-Axis settings'), - '#type' => 'radios', - '#options' => array( - 1 => t('Inherit primary of parent display'), - 0 => t('Create a secondary axis'), - ), - '#default_value' => $this->get_option('inherit_yaxis'), - '#description' => t('In most charts, the X and Y axis from the parent display are both shared with each attached child chart. However, if this chart is going to use a different unit of measurement, a secondary axis may be added on the opposite side of the normal Y-axis.'), - ); - break; - - } - } - - /** - * Perform any necessary changes to the form values prior to storage. - * There is no need for this function to actually store the data. - */ - function options_submit(&$form, &$form_state) { - // It is very important to call the parent function here: - parent::options_submit($form, $form_state); - switch ($form_state['section']) { - case 'parent_display': - case 'inherit_yaxis': - $this->set_option($form_state['section'], $form_state['values'][$form_state['section']]); - break; - } - } -} - -- GitLab