Skip to content

Commit 300b32b

Browse files
committed
General issue fix
1 parent 9cc361d commit 300b32b

11 files changed

Lines changed: 291 additions & 496 deletions

File tree

asset/css/nodegraph.css

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
#network-tooltip {
1111
position: absolute;
1212
background-color: #fff;
13-
max-width: 200px;
13+
max-width: 250px;
14+
max-height: 400px;
1415
word-wrap: break-word;
1516
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4);
1617
border-radius: 5px;
@@ -19,8 +20,12 @@
1920
pointer-events: none;
2021
z-index: 1000;
2122
display: none;
23+
line-height: normal;
24+
pointer-events: auto;
25+
overflow-y: auto;
2226
}
2327

28+
2429
#network-tooltip p {
2530
margin-top: 5px;
2631
margin-bottom: 5px;

config/module.config.php

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,28 @@
1616
\NodeGraph\Form\Fieldset\GroupByFieldset::class => \Laminas\ServiceManager\Factory\InvokableFactory::class,
1717
\NodeGraph\Form\Fieldset\NodeColorsFieldset::class => \Laminas\ServiceManager\Factory\InvokableFactory::class,
1818
\NodeGraph\Form\Fieldset\NodeColorPairFieldset::class => \Laminas\ServiceManager\Factory\InvokableFactory::class,
19-
\NodeGraph\Form\Fieldset\NodeIconsFieldset::class => \Laminas\ServiceManager\Factory\InvokableFactory::class,
20-
\NodeGraph\Form\Fieldset\NodeIconPairFieldset::class => \Laminas\ServiceManager\Factory\InvokableFactory::class,
2119
],
2220
],
23-
];
21+
'router' => [
22+
'routes' => [
23+
'node-graph-ajax' => [
24+
'type' => \Laminas\Router\Http\Segment::class,
25+
'options' => [
26+
'route' => '/node-graph/ajax/:action',
27+
'defaults' => [
28+
'controller' => \NodeGraph\Controller\AjaxController::class,
29+
'action' => 'popup-extra',
30+
],
31+
],
32+
],
33+
],
34+
],
2435

36+
// + controller factory
37+
'controllers' => [
38+
'factories' => [
39+
\NodeGraph\Controller\AjaxController::class => \Laminas\ServiceManager\Factory\InvokableFactory::class,
40+
],
41+
],
42+
];
2543

26-
// To do: Node colors and icons if preset. remove button wont work

functions.php

Lines changed: 7 additions & 167 deletions
Original file line numberDiff line numberDiff line change
@@ -117,36 +117,6 @@ function sigmaBuildColorMap(array $items, string $groupBy, ?string $propTerm, ar
117117
return $map;
118118
}
119119

120-
/**
121-
* Build icon map for current groupBy, with a default icon for gaps.
122-
* @return array<string,string> key => icon class (Font Awesome)
123-
*/
124-
function sigmaBuildIconMap(array $items, string $groupBy, ?string $propTerm, array $nodeIcons, string $defaultIcon = 'fas fa-circle'): array
125-
{
126-
$rows = sigmaNormalizeRows($nodeIcons);
127-
$map = [];
128-
129-
foreach ($rows as $row) {
130-
$key = '';
131-
if ($groupBy === 'resource_class') $key = (string) ($row['resource_class'] ?? '');
132-
elseif ($groupBy === 'resource_template') $key = (string) ($row['resource_template'] ?? '');
133-
elseif ($groupBy === 'property_value') $key = (string) ($row['property_value'] ?? '');
134-
if ($key === '') continue;
135-
136-
$icon = trim((string) ($row['icon'] ?? ''));
137-
$map[$key] = $icon !== '' ? $icon : $defaultIcon;
138-
}
139-
140-
// Ensure every observed group has an icon
141-
foreach ($items as $item) {
142-
if (!$item instanceof ItemRepresentation) continue;
143-
$key = sigmaGetItemGroupKey($item, $groupBy, $propTerm);
144-
if ($key === '') continue;
145-
if (!isset($map[$key])) $map[$key] = $defaultIcon;
146-
}
147-
148-
return $map;
149-
}
150120

151121
/**
152122
* Safe thumbnail URL (best-effort).
@@ -181,148 +151,25 @@ function sigmaBuildPopUpContent(ItemRepresentation $item, array $popupConfig)
181151
return htmlspecialchars((string) $s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
182152
};
183153

154+
$itemUrl = (string) $item->siteUrl();
155+
// Title
184156
if (in_array('title', $popupConfig, true)) {
185-
$popupContent .= '<p style="font-size:10px">' . $e($item->displayTitle()) . '</p>';
157+
$title = $e($item->displayTitle());
158+
$popupContent .= $itemUrl
159+
? '<p style="font-size:10px"><a href="' . $e($itemUrl) . '" target="_blank" rel="noopener">' . $title . '</a></p>'
160+
: '<p style="font-size:10px">' . $title . '</p>';
186161
}
187162

188163
// Thumbnail
189164
if (in_array('thumbnail', $popupConfig, true)) {
190165
$thumbnail = sigmaGetItemThumbnailUrl($item);
191166
if ($thumbnail) {
192167
$popupContent .= '<div>'
193-
. '<img src="' . $e($thumbnail) . '" alt="" style="max-width:100px;max-height:100px" />'
168+
. '<img src="' . $e($thumbnail) . '" alt="" style="width:100%;height:auto;display:block" />'
194169
. '</div>';
195170
}
196171
}
197172

198-
// Metadata: all non-resource values (literal, uri), each in its own <p>
199-
if (in_array('metadata', $popupConfig, true)) {
200-
$bundle = $item->values(); // keyed by term
201-
202-
foreach ($bundle as $term => $propData) {
203-
// Normalize to: $prop (PropertyRepresentation|null), $vals (ValueRepresentation[])
204-
$prop = null;
205-
$vals = [];
206-
207-
if (is_array($propData) && array_key_exists('values', $propData)) {
208-
// Shape #1: ['property' => PropertyRepresentation|array, 'values' => ValueRepresentation[]]
209-
$vals = is_array($propData['values']) ? $propData['values'] : [];
210-
if (isset($propData['property']) && $propData['property'] instanceof PropertyRepresentation) {
211-
$prop = $propData['property'];
212-
}
213-
} elseif (is_array($propData) && isset($propData[0]) && $propData[0] instanceof ValueRepresentation) {
214-
// Shape #2: ValueRepresentation[] directly
215-
$vals = $propData;
216-
} else {
217-
// Unknown shape — skip
218-
continue;
219-
}
220-
221-
// Label: prefer PropertyRepresentation->label(), else fall back to a friendly term
222-
$label = $prop instanceof PropertyRepresentation ? $prop->label() : preg_replace('~^.+:~', '', (string) $term);
223-
224-
foreach ($vals as $v) {
225-
if (!$v instanceof ValueRepresentation) {
226-
// Very old shapes could be arrays; keep a defensive fallback:
227-
$type = is_array($v) ? ($v['type'] ?? '') : '';
228-
if ($type === 'resource' || $type === 'resource:item' || $type === 'resource:media') {
229-
continue;
230-
}
231-
$text = '';
232-
if (is_array($v)) {
233-
$text = isset($v['@value']) ? (string) $v['@value']
234-
: (isset($v['@id']) ? (string) $v['@id'] : (string) ($v['value'] ?? ''));
235-
}
236-
if ($text !== '') {
237-
$popupContent .= '<p style="font-size:10px"><strong>' . $e($label) . ':</strong> ' . $e($text) . '</p>';
238-
}
239-
continue;
240-
}
241-
242-
// Skip resource links; we only want non-resource objects
243-
$type = $v->type();
244-
if ($type === 'resource' || $type === 'resource:item' || $type === 'resource:media') {
245-
continue;
246-
}
247-
248-
// Literal and URI
249-
$text = (string) $v->value();
250-
if ($text === '') {
251-
continue;
252-
}
253-
254-
$popupContent .= '<p style="font-size:10px"><strong>'
255-
. $e(sigmaUcfirst($label)) . ':</strong> ' . $e($text) . '</p>';
256-
}
257-
}
258-
}
259-
260-
// Relationships: only resource-valued links to *items*
261-
if (in_array('relationships', $popupConfig, true)) {
262-
$bundle = $item->values(); // keyed by term
263-
264-
foreach ($bundle as $term => $propData) {
265-
// Normalize shapes -> $prop, $vals
266-
$prop = null;
267-
$vals = [];
268-
269-
if (is_array($propData) && array_key_exists('values', $propData)) {
270-
$vals = is_array($propData['values']) ? $propData['values'] : [];
271-
if (isset($propData['property']) && $propData['property'] instanceof PropertyRepresentation) {
272-
$prop = $propData['property'];
273-
}
274-
} elseif (is_array($propData) && isset($propData[0]) && $propData[0] instanceof ValueRepresentation) {
275-
$vals = $propData;
276-
} else {
277-
continue;
278-
}
279-
280-
$label = $prop instanceof PropertyRepresentation
281-
? $prop->label()
282-
: preg_replace('~^.+:~', '', (string) $term);
283-
284-
$links = [];
285-
foreach ($vals as $v) {
286-
if (!$v instanceof ValueRepresentation) {
287-
// Legacy array fallback
288-
$type = is_array($v) ? ($v['type'] ?? '') : '';
289-
if ($type !== 'resource' && $type !== 'resource:item') {
290-
continue;
291-
}
292-
// No reliable way to resolve resource from legacy array; skip
293-
continue;
294-
}
295-
296-
// Accept both 'resource:item' and generic 'resource' (check valueResource)
297-
$type = $v->type();
298-
if (($type !== 'resource' && $type !== 'resource:item') || !$v->valueResource()) {
299-
continue;
300-
}
301-
302-
$vr = $v->valueResource();
303-
if (!$vr instanceof ItemRepresentation) {
304-
continue; // only link to items
305-
}
306-
307-
$title = $vr->displayTitle();
308-
$url = method_exists($vr, 'siteUrl') ? $vr->siteUrl() : '';
309-
310-
if ($url) {
311-
$links[] = '<a href="' . $e($url) . '" target="_blank" rel="noopener">' . $e($title) . '</a>';
312-
} else {
313-
$links[] = $e($title);
314-
}
315-
}
316-
317-
if ($links) {
318-
$popupContent .= '<p style="font-size:10px"><strong>'
319-
. $e(sigmaUcfirst($label)) . ':</strong> '
320-
. implode(', ', $links)
321-
. '</p>';
322-
}
323-
}
324-
}
325-
326173
return $popupContent;
327174
}
328175

@@ -331,7 +178,6 @@ function sigmaGenerateGraph(array $items, array $opts = []): array
331178
$groupBy = $opts['groupBy'] ?? 'resource_class';
332179
$propTerm = $opts['propTerm'] ?? null;
333180
$colorsRaw = $opts['nodeColors'] ?? [];
334-
$iconsRaw = $opts['nodeIcons'] ?? [];
335181
$relProps = array_values(array_filter($opts['relationshipProperties'] ?? []));
336182
$excludeIsolated = !empty($opts['excludeWithoutRelationships']);
337183
$sizeMin = isset($opts['sizeMin']) ? (int) $opts['sizeMin'] : 3;
@@ -341,7 +187,6 @@ function sigmaGenerateGraph(array $items, array $opts = []): array
341187

342188
// Build maps
343189
$colorMap = sigmaBuildColorMap($items, $groupBy, $propTerm, $colorsRaw);
344-
$iconMap = sigmaBuildIconMap($items, $groupBy, $propTerm, $iconsRaw);
345190

346191
// Pre-index items by id for quick lookups
347192
$itemById = [];
@@ -359,7 +204,6 @@ function sigmaGenerateGraph(array $items, array $opts = []): array
359204
$short = (mb_strlen($label, 'UTF-8') > 25) ? (mb_substr($label, 0, 22, 'UTF-8') . '') : $label;
360205

361206
$color = $key !== '' && isset($colorMap[$key]) ? $colorMap[$key] : sigmaColorFromKey((string) $id);
362-
$icon = $key !== '' && isset($iconMap[$key]) ? $iconMap[$key] : 'fas fa-circle';
363207

364208
if ($key !== '' && !isset($groupLabels[$key])) {
365209
$groupLabels[$key] = sigmaResolveGroupLabelFromItem($item, $groupBy, $propTerm);
@@ -380,12 +224,9 @@ function sigmaGenerateGraph(array $items, array $opts = []): array
380224
'size' => $sizeMin,
381225
'color' => $color,
382226
'originalColor' => $color,
383-
'icon' => $icon,
384227
'groupKey' => $key,
385228
'popupContent' => sigmaBuildPopUpContent($item, $popupConfig),
386229
'link' => method_exists($item, 'siteUrl') ? $item->siteUrl() : null,
387-
'type' => 'image',
388-
"image" => 'https://cdn0.iconfinder.com/data/icons/30-hardware-line-icons/64/Server-128.png',
389230
];
390231
}
391232

@@ -528,7 +369,6 @@ function sigmaGenerateGraph(array $items, array $opts = []): array
528369

529370
$legendMap[$groupLabels[$key] ?? (string) $key] = [
530371
'color' => $colorMap[$key] ?? sigmaColorFromKey($key),
531-
'icon' => $iconMap[$key] ?? 'fas fa-circle',
532372
];
533373
}
534374

0 commit comments

Comments
 (0)