1<?php
2/*
3** Zabbix
4** Copyright (C) 2001-2021 Zabbix SIA
5**
6** This program is free software; you can redistribute it and/or modify
7** it under the terms of the GNU General Public License as published by
8** the Free Software Foundation; either version 2 of the License, or
9** (at your option) any later version.
10**
11** This program is distributed in the hope that it will be useful,
12** but WITHOUT ANY WARRANTY; without even the implied warranty of
13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14** GNU General Public License for more details.
15**
16** You should have received a copy of the GNU General Public License
17** along with this program; if not, write to the Free Software
18** Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19**/
20
21
22abstract class CGraphDraw {
23
24	public function __construct($type = GRAPH_TYPE_NORMAL) {
25		$this->stime = null;
26		$this->fullSizeX = null;
27		$this->fullSizeY = null;
28		$this->m_minY = null;
29		$this->m_maxY = null;
30		$this->data = [];
31		$this->items = null;
32		$this->min = null;
33		$this->max = null;
34		$this->avg = null;
35		$this->clock = null;
36		$this->count = null;
37		$this->header = null;
38		$this->from_time = null;
39		$this->to_time = null;
40		$this->colors = null;
41		$this->colorsrgb = null;
42		$this->im = null;
43		$this->period = SEC_PER_HOUR;
44		$this->from = 0;
45		$this->sizeX = 900; // default graph size X
46		$this->sizeY = 200; // default graph size Y
47		$this->shiftXleft = 100;
48		$this->shiftXright = 50;
49		$this->shiftXCaption = 0;
50		$this->shiftY = 36;
51		$this->num = 0;
52		$this->type = $type; // graph type
53		$this->drawLegend = 1;
54		$this->axis_valuetype = []; // overal items type (int/float)
55		$this->graphtheme = [
56			'theme' => 'blue-theme',
57			'textcolor' => '1F2C33',
58			'highlightcolor' => 'E33734',
59			'backgroundcolor' => 'FFFFFF',
60			'graphcolor' => 'FFFFFF',
61			'gridcolor' => 'CCD5D9',
62			'maingridcolor' => 'ACBBC2',
63			'gridbordercolor' => 'ACBBC2',
64			'nonworktimecolor' => 'EBEBEB',
65			'leftpercentilecolor' => '429E47',
66			'righttpercentilecolor' => 'E33734'
67		];
68		$this->applyGraphTheme();
69	}
70
71	public function initColors() {
72		// red, green, blue, alpha
73		$this->colorsrgb = [
74			'Red'				=> [255, 0, 0, 50],
75			'Dark Red'			=> [150, 0, 0, 50],
76			'Green'				=> [0, 255, 0, 50],
77			'Dark Green'		=> [0, 150, 0, 50],
78			'Blue'				=> [0, 0, 255, 50],
79			'Dark Blue'			=> [0, 0, 150, 50],
80			'Yellow'			=> [255, 255, 0, 50],
81			'Dark Yellow'		=> [150, 150, 0, 50],
82			'Cyan'				=> [0, 255, 255, 50],
83			'Dark Cyan'			=> [0, 150, 150, 50],
84			'Black'				=> [0, 0, 0, 50],
85			'Gray'				=> [150, 150, 150, 50],
86			'White'				=> [255, 255, 255],
87			'Dark Red No Alpha'	=> [150, 0, 0],
88			'Black No Alpha'	=> [0, 0, 0],
89			'HistoryMinMax'		=> [90, 150, 185, 50],
90			'HistoryMax'		=> [255, 100, 100, 50],
91			'HistoryMin'		=> [50, 255, 50, 50],
92			'HistoryAvg'		=> [50, 50, 50, 50],
93			'ValueMinMax'		=> [255, 255, 150, 50],
94			'ValueMax'			=> [255, 180, 180, 50],
95			'ValueMin'			=> [100, 255, 100, 50],
96			'Not Work Period'	=> [230, 230, 230],
97			'UnknownData'		=> [130, 130, 130, 50]
98		];
99
100		// i should rename no alpha to alpha at some point to get rid of some confusion
101		foreach ($this->colorsrgb as $name => $RGBA) {
102			if (isset($RGBA[3]) && function_exists('imagecolorexactalpha')
103					&& function_exists('imagecreatetruecolor') && @imagecreatetruecolor(1, 1)) {
104				$this->colors[$name] = imagecolorexactalpha($this->im, $RGBA[0], $RGBA[1], $RGBA[2], $RGBA[3]);
105			}
106			else {
107				$this->colors[$name] = imagecolorallocate($this->im, $RGBA[0], $RGBA[1], $RGBA[2]);
108			}
109		}
110	}
111
112	/**
113	 * Load the graph theme from the database.
114	 */
115	public function applyGraphTheme() {
116		$themes = DB::find('graph_theme', [
117			'theme' => getUserTheme(CWebUser::$data)
118		]);
119		if ($themes) {
120			$this->graphtheme = $themes[0];
121		}
122	}
123
124	public function showLegend($type = true) {
125		$this->drawLegend = $type;
126	}
127
128	public function setPeriod($period) {
129		$this->period = $period;
130	}
131
132	public function setSTime($stime) {
133		if ($stime > 19000000000000 && $stime < 21000000000000) {
134			$this->stime = zbxDateToTime($stime);
135		}
136		else {
137			$this->stime = $stime;
138		}
139	}
140
141	public function setFrom($from) {
142		$this->from = $from;
143	}
144
145	public function setWidth($value = null) {
146		// avoid sizex==0, to prevent division by zero later
147		if ($value == 0) {
148			$value = null;
149		}
150		if (is_null($value)) {
151			$value = 900;
152		}
153		$this->sizeX = $value;
154	}
155
156	public function setHeight($value = null) {
157		if ($value == 0) {
158			$value = null;
159		}
160		if (is_null($value)) {
161			$value = 900;
162		}
163		$this->sizeY = $value;
164	}
165
166	public function getLastValue($num) {
167		$data = &$this->data[$this->items[$num]['itemid']][$this->items[$num]['calc_type']];
168
169		if (isset($data)) {
170			for ($i = $this->sizeX - 1; $i >= 0; $i--) {
171				if (!empty($data['count'][$i])) {
172					switch ($this->items[$num]['calc_fnc']) {
173						case CALC_FNC_MIN:
174							return $data['min'][$i];
175						case CALC_FNC_MAX:
176							return $data['max'][$i];
177						case CALC_FNC_ALL:
178						case CALC_FNC_AVG:
179						default:
180							return $data['avg'][$i];
181					}
182				}
183			}
184		}
185
186		return 0;
187	}
188
189	public function drawRectangle() {
190		imagefilledrectangle($this->im, 0, 0,
191			$this->fullSizeX,
192			$this->fullSizeY,
193			$this->getColor($this->graphtheme['backgroundcolor'], 0)
194		);
195	}
196
197	public function period2str($period) {
198		return ' ('.zbx_date2age(0, $period).')';
199	}
200
201	public function drawHeader() {
202		if (!isset($this->header)) {
203			$str = $this->items[0]['hostname'].NAME_DELIMITER.$this->items[0]['name'];
204		}
205		else {
206			// TODO: graphs shouldn't resolve names themselves
207			$str = CMacrosResolverHelper::resolveGraphName($this->header, $this->items);
208		}
209
210		if ($this->period) {
211			$str .= $this->period2str($this->period);
212		}
213
214		// calculate largest font size that can fit graph header
215		// TODO: font size must be dynamic in other parts of the graph as well, like legend, timeline, etc
216		for ($fontsize = 11; $fontsize > 7; $fontsize--) {
217			$dims = imageTextSize($fontsize, 0, $str);
218			$x = $this->fullSizeX / 2 - ($dims['width'] / 2);
219
220			// most important information must be displayed, period can be out of the graph
221			if ($x < 2) {
222				$x = 2;
223			}
224			if ($dims['width'] <= $this->fullSizeX) {
225				break;
226			}
227		}
228
229		imageText($this->im, $fontsize, 0, $x, 24, $this->getColor($this->graphtheme['textcolor'], 0), $str);
230	}
231
232	public function setHeader($header) {
233		$this->header = $header;
234	}
235
236	public function drawLogo() {
237		imagestringup($this->im, 1,
238			$this->fullSizeX - 10,
239			$this->fullSizeY - 50,
240			ZABBIX_HOMEPAGE,
241			$this->getColor('Gray')
242		);
243	}
244
245	public function getColor($color, $alfa = 50) {
246		if (isset($this->colors[$color])) {
247			return $this->colors[$color];
248		}
249
250		return get_color($this->im, $color, $alfa);
251	}
252
253	public function getShadow($color, $alfa = 0) {
254		if (isset($this->colorsrgb[$color])) {
255			$red = $this->colorsrgb[$color][0];
256			$green = $this->colorsrgb[$color][1];
257			$blue = $this->colorsrgb[$color][2];
258		}
259		else {
260			list($red, $green, $blue) = hex2rgb($color);
261		}
262
263		if ($this->sum > 0) {
264			$red = (int)($red * 0.6);
265			$green = (int)($green * 0.6);
266			$blue = (int)($blue * 0.6);
267		}
268
269		$RGB = [$red, $green, $blue];
270
271		if (isset($alfa) && function_exists('imagecolorexactalpha') && function_exists('imagecreatetruecolor')
272				&& @imagecreatetruecolor(1, 1)) {
273			return imagecolorexactalpha($this->im, $RGB[0], $RGB[1], $RGB[2], $alfa);
274		}
275
276		return imagecolorallocate($this->im, $RGB[0], $RGB[1], $RGB[2]);
277	}
278}
279