OK - Here’s my code.
In /manager/frames/3ldr.php
This is replacement for the makeHTML function.
My basic idea is to 1st extract every docID plus its parents from the db and store as array - This is the first additional db call that I make.
Next make a call to get the every docID that the user has access to. This is the 2nd additional call
Now build a list of all the docIDs that lie between the accessible docID and the $parent docID as passed to the function - these are the docs which need to be displayed to allow the user to navigate to his doc.
Modify the original sql statement so that it requests these extra docs regardless of their edit/access permissions.
At the moment I just build a comma separated string for the additional docIDs - There is probably a better / more efficient way to do this.
I have done some limited testing - and it seems to satisfy my needs (eg an editor with access only to docs which are 3/4 folders deep in the tree can browse to his folders, without seeing the whole tree.
Hope this helps !
My only other thoughts are:-
On a large site the 1st request to get every docID plus parent could be a problem.
Did I read recently that there is a global in the API which stores the tree?
If so, then I should probably use this instead of requesting it myself.
function makeHTML($indent,$parent,$expandAll)
{
global $icons;
global $modxDBConn, $output, $dbase, $table_prefix, $_lang, $opened, $opened2, $closed2; //added global vars
$pad = " ";
// setup spacer
$spacer = "";
for ($i = 1; $i <= $indent; $i++){
$spacer .= " ";
}
// Raymond: query other documents,set default sort order
$orderby = "isfolder DESC";
if(isset($_SESSION['tree_sortby']) && isset($_SESSION['tree_sortdir'])) {
$orderby = $_SESSION['tree_sortby']." ".$_SESSION['tree_sortdir'];
} else {
$_SESSION['tree_sortby'] = 'isfolder';
$_SESSION['tree_sortdir'] = 'DESC';
}
if($_SESSION['tree_sortby'] == 'isfolder') $orderby .= ", menuindex ASC, pagetitle";
//Raymond: end
$tblsc = $dbase.".".$table_prefix."site_content";
$tbldg = $dbase.".".$table_prefix."document_groups";
$tbldgn = $dbase.".".$table_prefix."documentgroup_names";
// get document groups for current user
if($_SESSION['mgrDocgroups']) $docgrp = implode(",",$_SESSION['mgrDocgroups']);
$access = "1='".$_SESSION['mgrRole']."' OR sc.privatemgr=0".
(!$docgrp ? "":" OR dg.document_group IN ($docgrp)");
//**********************
//wyn Fetch list of all documents plus parents
$MainAddList='';
$sql = "SELECT id, parent FROM $tblsc";
$result = mysql_query($sql, $modxDBConn);
//wyn store all docs and parents in array
while(list($id,$thisparent) = mysql_fetch_row($result))
{
$parentarray[$id]=$thisparent;
}
//wyn now select all docs the user is allowed to access
$sql = "SELECT DISTINCT sc.id
FROM $tblsc AS sc
LEFT JOIN $tbldg dg on dg.document = sc.id
WHERE ($access)
ORDER BY $orderby";
$result = mysql_query($sql, $modxDBConn);
//wyn go through each doc and generate a list of all its parents
//wyn at the moment I am just building a string of comma seperated id numbers.
//wyn There is probably a better way to do this !
while (list($id) = mysql_fetch_row($result)) {
$addlist='';
//wyn First go up through the tree, adding doc ids to $addlist until we reach $parent
while ($id) {
$thisparent=$parentarray[$id];
if ($thisparent==$parent) {
$addlist.=",$id,";
break;
}
$id=$thisparent;
}
//wyn Now add each element from $AddList into $MainAddList (if it is not already there)
if ($thisparent == $parent) {
if (strlen($addlist)) {
$addlist=str_replace(',,',',',$addlist);
$addlist=substr($addlist,1,-1);
$addlist=explode(',',$addlist);
foreach ($addlist as $thisid) {
if (strpos($MainAddList,",$thisid,")===false) {
$MainAddList .= ",$thisid,";
}
}
}
}
}
//wyn Tidy up $MainAddList to remove double commas etc
if (strlen($MainAddList)) {
$MainAddList=str_replace(',,',',',$MainAddList);
$MainAddList=substr($MainAddList,1,-1);
}
//******************************
$sql = "SELECT DISTINCT sc.id, pagetitle, parent, isfolder, published, deleted, type, menuindex, hidemenu, alias, contentType, privateweb, privatemgr
FROM $tblsc AS sc
LEFT JOIN $tbldg dg on dg.document = sc.id
WHERE ((parent=$parent)
AND ($access)) ";
//wyn Also make it fetch any docs included in $MainAddList
if (strlen($MainAddList)) {
$sql.="OR sc.id IN ($MainAddList) " ;
}
$sql .= "ORDER BY $orderby";
$result = mysql_query($sql, $modxDBConn);
if(mysql_num_rows($result)==0) {
$output .= '<div style="white-space: nowrap;">'.$spacer.$pad.'<img align="absmiddle" src="media/images/tree/deletedpage.gif" width="18" height="18"> <span class="emptyNode">'.$_lang['empty_folder'].'</span></div>';
}
while(list($id,$pagetitle,$parent,$isfolder,$published,$deleted,$type,$menuindex,$hidemenu,$alias,$contenttype,$privateweb,$privatemgr) = mysql_fetch_row($result))
{
$pagetitle = htmlspecialchars($pagetitle);
$pagetitleDisplay = $published==0 ? "<span class='unpublishedNode'>$pagetitle</span>" : ($hidemenu==1 ? "<span class='notInMenuNode'>$pagetitle</span>":"<span class='publishedNode'>$pagetitle</span>");
$pagetitleDisplay = $deleted==1 ? "<span class='deletedNode'>$pagetitle</span>" : $pagetitleDisplay;
$weblinkDisplay = $type=="reference" ? ' <img align="absmiddle" src="media/images/tree/web.gif">' : '' ;
$alt = !empty($alias) ? $_lang['alias'].": ".$alias : $_lang['alias'].": - ";
$alt.= "\n".$_lang['document_opt_menu_index'].": ".$menuindex;
$alt.= "\n".$_lang['document_opt_show_menu'].": ".($hidemenu==1 ? $_lang['no']:$_lang['yes']);
$alt.= "\n".$_lang['page_data_web_access'].": ".($privateweb ? $_lang['private']:$_lang['public']);
$alt.= "\n".$_lang['page_data_mgr_access'].": ".($privatemgr ? $_lang['private']:$_lang['public']);
if (!$isfolder) {
$icon='page';
if($privateweb||$privatemgr) $icon='page-secure';
else if(isset($icons[$contenttype])) $icon = $icons[$contenttype];
$output .= '<div id="node'.$id.'" p="'.$parent.'" style="white-space: nowrap;">'.$spacer.$pad.'<img id="p'.$id.'" align="absmiddle" title="'.$_lang['click_to_context'].'" style="cursor: pointer" src="media/images/tree/'.$icon.'.gif" width="18" height="18" onclick="showPopup('.$id.',\''.addslashes($pagetitle).'\',event);return false;" oncontextmenu="this.onclick(event);return false;" onmouseover="setCNS(this, 1)" onmouseout="setCNS(this, 0)" onmousedown="itemToChange='.$id.'; selectedObjectName=\''.addslashes($pagetitle).'\'; selectedObjectDeleted='.$deleted.';" /> ';
$output .= '<span p="'.$parent.'" onclick="treeAction('.$id.', \''.addslashes($pagetitle).'\'); setSelected(this);" onmouseover="setHoverClass(this, 1);" onmouseout="setHoverClass(this, 0);" class="treeNode" onmousedown="itemToChange='.$id.'; selectedObjectName=\''.addslashes($pagetitle).'\'; selectedObjectDeleted='.$deleted.';" oncontextmenu="document.getElementById(\'p'.$id.'\').onclick(event);return false;" title="'.addslashes($alt).'">'.$pagetitleDisplay.$weblinkDisplay.'</span> <small>('.$id.')</small></div>';
}
else {
//
// Jeroen add the expandAll 2 type for partial expansion
//
if ($expandAll ==1 || ($expandAll == 2 && in_array($id, $opened)))
{
if ($expandAll == 1) {
array_push($opened2, $id);
}
$icon='folderopen';
$output .= '<div id="node'.$id.'" p="'.$parent.'" style="white-space: nowrap;">'.$spacer.'<img id="s'.$id.'" align="absmiddle" style="cursor: pointer" src="media/images/tree/minusnode.gif" width="18" height="18" onclick="toggleNode(this,'.($indent+1).','.$id.',0); return false;" oncontextmenu="this.onclick(event); return false;" /> <img id="f'.$id.'" align="absmiddle" title="'.$_lang['click_to_context'].'" style="cursor: pointer" src="media/images/tree/'.$icon.'.gif" width="18" height="18" onclick="showPopup('.$id.',\''.addslashes($pagetitle).'\',event);return false;" oncontextmenu="this.onclick(event);return false;" onmouseover="setCNS(this, 1)" onmouseout="setCNS(this, 0)" onmousedown="itemToChange='.$id.'; selectedObjectName=\''.addslashes($pagetitle).'\'; selectedObjectDeleted='.$deleted.';" /> ';
$output .= '<span onclick="treeAction('.$id.', \''.addslashes($pagetitle).'\'); setSelected(this);" onmouseover="setHoverClass(this, 1);" onmouseout="setHoverClass(this, 0);" class="treeNode" onmousedown="itemToChange='.$id.'; selectedObjectName=\''.addslashes($pagetitle).'\'; selectedObjectDeleted='.$deleted.';" oncontextmenu="document.getElementById(\'f'.$id.'\').onclick(event);return false;" title="'.addslashes($alt).'">'.$pagetitleDisplay.$weblinkDisplay.'</span> <small>('.$id.')</small><div style="display:block">';
makeHTML($indent+1,$id,$expandAll);
$output .= '</div></div>';
}
else {
$icon='folder';
$output .= '<div id="node'.$id.'" p="'.$parent.'" style="white-space: nowrap;">'.$spacer.'<img id="s'.$id.'" align="absmiddle" style="cursor: pointer" src="media/images/tree/plusnode.gif" width="18" height="18" onclick="toggleNode(this,'.($indent+1).','.$id.',0); return false;" oncontextmenu="this.onclick(event); return false;" /> <img id="f'.$id.'" title="'.$_lang['click_to_context'].'" align="absmiddle" style="cursor: pointer" src="media/images/tree/'.$icon.'.gif" width="18" height="18" onclick="showPopup('.$id.',\''.addslashes($pagetitle).'\',event);return false;" oncontextmenu="this.onclick(event);return false;" onmouseover="setCNS(this, 1)" onmouseout="setCNS(this, 0)" onmousedown="itemToChange='.$id.'; selectedObjectName=\''.addslashes($pagetitle).'\'; selectedObjectDeleted='.$deleted.';" /> ';
$output .= '<span onclick="treeAction('.$id.', \''.addslashes($pagetitle).'\'); setSelected(this);" onmouseover="setHoverClass(this, 1);" onmouseout="setHoverClass(this, 0);" class="treeNode" onmousedown="itemToChange='.$id.'; selectedObjectName=\''.addslashes($pagetitle).'\'; selectedObjectDeleted='.$deleted.';" oncontextmenu="document.getElementById(\'f'.$id.'\').onclick(event);return false;" title="'.addslashes($alt).'">'.$pagetitleDisplay.$weblinkDisplay.'</span> <small>('.$id.')</small><div style="display:none"></div></div>';
array_push($closed2, $id);
}
//
// Jeroen end
//
}
//
// Jeroen stores vars in Javascript
//
if ($expandAll == 1) {
echo '<script language="JavaScript"> ';
foreach ($opened2 as $item) {
printf("parent.openedArray[%d] = 1; ", $item);
}
echo '</script> ';
} elseif ($expandAll == 0) {
echo '<script language="JavaScript"> ';
foreach ($closed2 as $item) {
printf("parent.openedArray[%d] = 0; ", $item);
}
echo '</script> ';
}
//
// Jeroen end
//
}
}