Skip to content

Commit 9d4cffc

Browse files
author
Alex Damsted
committed
[#225] Fix Totara compatibility bug with the core get_directory_size function
1 parent c358a3e commit 9d4cffc

File tree

2 files changed

+157
-15
lines changed

2 files changed

+157
-15
lines changed

classes/check/dirsizes.php

Lines changed: 79 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,9 @@
1717
/**
1818
* Dir sizes performance check.
1919
*
20-
* @package tool_heartbeat
21-
* @copyright 2023 Brendan Heywood <brendan@catalyst-au.net>
22-
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
23-
*
20+
* @package tool_heartbeat
21+
* @copyright 2023 Brendan Heywood <brendan@catalyst-au.net>
22+
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2423
*/
2524

2625
namespace tool_heartbeat\check;
@@ -30,12 +29,13 @@
3029
/**
3130
* Dir sizes performance check.
3231
*
33-
* @copyright 2023
34-
* @author Brendan Heywood <brendan@catalyst-au.net>
35-
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
32+
* @copyright 2023
33+
* @author Brendan Heywood <brendan@catalyst-au.net>
34+
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3635
*/
3736
class dirsizes extends check {
3837

38+
3939
/**
4040
* Get Result.
4141
*
@@ -44,10 +44,10 @@ class dirsizes extends check {
4444
public function get_result(): result {
4545
global $CFG;
4646

47-
$sizedataroot = get_directory_size($CFG->dataroot);
48-
$summary = $sizedataroot;
47+
$sizedataroot = $this->dirsize('dataroot');
48+
$summary = $this->dirsize_totara('dataroot');
4949
$details = "Shared paths:<br>";
50-
$details .= '$CFG->dataroot = ' . display_size($sizedataroot);
50+
$details .= $sizedataroot;
5151

5252
$details .= $this->dirsize('themedir');
5353
$details .= $this->dirsize('tempdir');
@@ -61,19 +61,83 @@ public function get_result(): result {
6161
return new result(result::INFO, $summary, $details);
6262
}
6363
/**
64-
* Get a paths sizet
65-
* @param string $cfg the path to check
66-
* @return string size for a path as html
64+
* Get a path's size
65+
*
66+
* @param string $cfg the path to check
67+
* @return string $size for a path as html
6768
*/
6869
private function dirsize(string $cfg) {
6970
global $CFG;
7071
if (!property_exists($CFG, $cfg)) {
7172
return "<br>\$CFG->$cfg not in use";
7273
}
73-
$path = $CFG->{$cfg};
74-
$size = get_directory_size($path);
74+
75+
// If Totara, use Totara-compatible function.
76+
if (!empty($CFG->totara_version)) {
77+
$size = $this->dirsize_totara($cfg);
78+
} else {
79+
$path = $CFG->{$cfg};
80+
$size = get_directory_size($path);
81+
}
7582

7683
return "<br>\$CFG->{$cfg} = " . display_size($size);
7784
}
7885

86+
/**
87+
* Get a path's size (compatible with Totara)
88+
*
89+
* @param string $cfg the path to check
90+
* @return int $size size for a path as an integer
91+
*/
92+
private function dirsize_totara(string $cfg): int {
93+
global $CFG;
94+
if (!property_exists($CFG, $cfg)) {
95+
return 0;
96+
}
97+
$rootdir = $CFG->{$cfg};
98+
99+
return $this->dirsize_totara_helper($rootdir);
100+
}
101+
102+
/**
103+
* Recursively calculate the size of a directory (Totara-compatible).
104+
*
105+
* This replicates Moodle core's get_directory_size() logic,
106+
* but avoids using it directly for Totara compatibility.
107+
*
108+
* @param string $dir The directory path to measure
109+
* @return int Total size in bytes
110+
*/
111+
private function dirsize_totara_helper(string $dir): int {
112+
if (!is_dir($dir)) {
113+
return 0;
114+
}
115+
116+
$size = 0;
117+
if (!$dh = @opendir($dir)) {
118+
return 0;
119+
}
120+
121+
while (false !== ($file = readdir($dh))) {
122+
// Skip hidden files and CVS dirs.
123+
if ($file[0] === '.' || $file === 'CVS') {
124+
continue;
125+
}
126+
127+
$fullfile = $dir . '/' . $file;
128+
129+
if (is_dir($fullfile)) {
130+
// Recurse into subdirectory.
131+
$size += $this->dirsize_totara_helper($fullfile);
132+
} else {
133+
$filesize = filesize($fullfile);
134+
if ($filesize !== false) {
135+
$size += $filesize;
136+
}
137+
}
138+
}
139+
140+
closedir($dh);
141+
return $size;
142+
}
79143
}

tests/dirsizes_totara_test.php

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
// This file is part of Moodle - http://moodle.org/
3+
//
4+
// Moodle is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// Moodle is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU General Public License
15+
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16+
17+
namespace tool_heartbeat\check;
18+
19+
/**
20+
* Test class for Totara tool_heartbeat\check\dirsizes
21+
*
22+
* @package tool_heartbeat
23+
* @author Alex Damsted <alexdamsted@catalyst-au.net>
24+
* @copyright 2025, Catalyst IT
25+
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26+
*/
27+
28+
final class dirsizes_totara_test extends \advanced_testcase {
29+
30+
protected function setUp(): void {
31+
parent::setUp();
32+
$this->resetAfterTest(true);
33+
}
34+
35+
public function test_dirsize_totara_counts_files(): void {
36+
global $CFG;
37+
$dir = make_request_directory('tool_heartbeat_test1');
38+
$CFG->tempdir = $dir;
39+
40+
// Create two test files with known sizes.
41+
file_put_contents($dir . '/file1.txt', str_repeat('a', 100));
42+
file_put_contents($dir . '/file2.txt', str_repeat('b', 200));
43+
44+
$check = new dirsizes();
45+
$size = $this->invokeMethod($check, 'dirsize_totara', ['tempdir']);
46+
47+
$this->assertEquals(300, $size);
48+
}
49+
50+
public function test_dirsize_totara_counts_nested_files(): void {
51+
global $CFG;
52+
53+
$dir = make_request_directory('tool_heartbeat_test2');
54+
$CFG->tempdir = $dir;
55+
56+
// Create subdirectory with file.
57+
$subdir = $dir . '/subdir';
58+
check_dir_exists($subdir);
59+
file_put_contents($subdir . '/nested.txt', str_repeat('x', 150));
60+
61+
$check = new dirsizes();
62+
$size = $this->invokeMethod($check, 'dirsize_totara', ['tempdir']);
63+
64+
$this->assertGreaterThanOrEqual(150, $size);
65+
}
66+
67+
/**
68+
* Helper to call private methods.
69+
*/
70+
protected function invokemethod($object, string $method, array $args = []) {
71+
$reflection = new \ReflectionClass(get_class($object));
72+
$method = $reflection->getMethod($method);
73+
$method->setAccessible(true);
74+
return $method->invokeArgs($object, $args);
75+
}
76+
}
77+
78+

0 commit comments

Comments
 (0)