diff --git a/conlite/classes/gui/class.page.php b/conlite/classes/gui/class.page.php new file mode 100644 index 0000000..c363be4 --- /dev/null +++ b/conlite/classes/gui/class.page.php @@ -0,0 +1,15 @@ +reset(); -if ($action == "remove_assignments") -{ - $sql = "DELETE FROM ".$cfg["tab"]["cat_art"]." WHERE idart = '".Contenido_Security::toInteger($idart)."' AND idcat != '".Contenido_Security::toInteger($idcat)."'"; - $db->query($sql); +if ($action == "remove_assignments") { + $sql = "DELETE FROM " . $cfg["tab"]["cat_art"] . " WHERE idart = '" . Contenido_Security::toInteger($idart) . "' AND idcat != '" . Contenido_Security::toInteger($idcat) . "'"; + $db->query($sql); } -if ($action == "con_newart" && $newart != true) -{ - // nothing to be done here ?! -} -else { - $disabled = ""; +if ($action == "con_newart" && $newart != true) { + // nothing to be done here ?! +} else { + $disabled = ""; - if ($perm->have_perm_area_action($area, "con_edit") || - $perm->have_perm_area_action_item($area,"con_edit", $idcat)) { + if ($perm->have_perm_area_action($area, "con_edit") || $perm->have_perm_area_action_item($area, "con_edit", $idcat)) { - $sql = "SELECT * FROM ".$cfg["tab"]["cat_art"]." WHERE idart = '".Contenido_Security::toInteger($idart)."' AND idcat = '".Contenido_Security::toInteger($idcat)."'"; - $db->query($sql); - $db->next_record(); + $sql = "SELECT * FROM " . $cfg["tab"]["cat_art"] . " WHERE idart = '" . Contenido_Security::toInteger($idart) . "' AND idcat = '" . Contenido_Security::toInteger($idcat) . "'"; + $db->query($sql); + $db->next_record(); - if ($cfg["is_start_compatible"] == true) - { - $tmp_is_start = $db->f("is_start"); - } - - $tmp_cat_art = $db->f("idcatart"); + if ($cfg["is_start_compatible"] == true) { + $tmp_is_start = $db->f("is_start"); + } - $sql = "SELECT * FROM ".$cfg["tab"]["art_lang"]." WHERE idart = '".Contenido_Security::toInteger($idart)."' AND idlang = '".Contenido_Security::toInteger($lang)."'"; + $tmp_cat_art = $db->f("idcatart"); - $db->query($sql); - $db->next_record(); + $sql = "SELECT * FROM " . $cfg["tab"]["art_lang"] . " WHERE idart = '" . Contenido_Security::toInteger($idart) . "' AND idlang = '" . Contenido_Security::toInteger($lang) . "'"; - if ($cfg["is_start_compatible"] == false) - { - $tmp_is_start = isStartArticle($db->f("idartlang"), $idcat, $lang); - } + $db->query($sql); + $db->next_record(); - if ( $db->f("created") ) { + if ($cfg["is_start_compatible"] == false) { + $tmp_is_start = isStartArticle($db->f("idartlang"), $idcat, $lang); + } - //****************** this art was edited before ******************** - $tmp_firstedit = 0; - $tmp_idartlang = $db->f("idartlang"); - $tmp_page_title = Contenido_Security::unFilter(stripslashes($db->f("pagetitle"))); - $tmp_idlang = $db->f("idlang"); - $tmp_title = Contenido_Security::unFilter($db->f("title")); - $tmp_urlname = Contenido_Security::unFilter($db->f("urlname")); // plugin Advanced Mod Rewrite - edit by stese - $tmp_artspec = $db->f("artspec"); - $tmp_summary = Contenido_Security::unFilter($db->f("summary")); - $tmp_created = $db->f("created"); - $tmp_lastmodified = $db->f("lastmodified"); - $tmp_author = $db->f("author"); - $tmp_modifiedby = $db->f("modifiedby"); - $tmp_online = $db->f("online"); - $tmp_published = $db->f("published"); - $tmp_publishedby = $db->f("publishedby"); - $tmp_datestart = $db->f("datestart"); - $tmp_dateend = $db->f("dateend"); - $tmp_sort = $db->f("artsort"); - $tmp_movetocat = $db->f("time_move_cat"); - $tmp_targetcat = $db->f("time_target_cat"); - $tmp_onlineaftermove = $db->f("time_online_move"); - $tmp_usetimemgmt = $db->f("timemgmt"); - $tmp_locked = $db->f("locked"); + if ($db->f("created")) { - $tmp_redirect_checked = ($db->f("redirect") == '1') ? 'checked' : ''; - $tmp_redirect_url = ($db->f("redirect_url") != '0') ? $db->f("redirect_url") : "http://"; - $tmp_external_redirect_checked = ($db->f("external_redirect") == '1') ? 'checked' : ''; + //****************** this art was edited before ******************** + $tmp_firstedit = 0; + $tmp_idartlang = $db->f("idartlang"); + $tmp_page_title = Contenido_Security::unFilter(stripslashes($db->f("pagetitle"))); + $tmp_idlang = $db->f("idlang"); + $tmp_title = Contenido_Security::unFilter($db->f("title")); + $tmp_urlname = Contenido_Security::unFilter($db->f("urlname")); // plugin Advanced Mod Rewrite - edit by stese + $tmp_artspec = $db->f("artspec"); + $tmp_summary = Contenido_Security::unFilter($db->f("summary")); + $tmp_created = $db->f("created"); + $tmp_lastmodified = $db->f("lastmodified"); + $tmp_author = $db->f("author"); + $tmp_modifiedby = $db->f("modifiedby"); + $tmp_online = $db->f("online"); + $tmp_published = $db->f("published"); + $tmp_publishedby = $db->f("publishedby"); + $tmp_datestart = $db->f("datestart"); + $tmp_dateend = $db->f("dateend"); + $tmp_sort = $db->f("artsort"); + $tmp_movetocat = $db->f("time_move_cat"); + $tmp_targetcat = $db->f("time_target_cat"); + $tmp_onlineaftermove = $db->f("time_online_move"); + $tmp_usetimemgmt = $db->f("timemgmt"); + $tmp_locked = $db->f("locked"); - $idtplinput = $db->f("idtplinput"); + $tmp_redirect_checked = ($db->f("redirect") == '1') ? 'checked' : ''; + $tmp_redirect_url = ($db->f("redirect_url") != '0') ? $db->f("redirect_url") : "http://"; + $tmp_external_redirect_checked = ($db->f("external_redirect") == '1') ? 'checked' : ''; - if ($tmp_modifiedby == "") - { - $tmp_modifiedby = $tmp_author; - } + $idtplinput = $db->f("idtplinput"); - $col = new InUseCollection; + if ($tmp_modifiedby == "") { + $tmp_modifiedby = $tmp_author; + } - /* Remove all own marks */ - $col->removeSessionMarks($sess->id); + $col = new InUseCollection; - if (($obj = $col->checkMark("article", $tmp_idartlang)) === false) - { - $col->markInUse("article", $tmp_idartlang, $sess->id, $auth->auth["uid"]); - $inUse = false; - $disabled = ""; - } else { - - $vuser = new User; - $vuser->loadUserByUserID($obj->get("userid")); - $inUseUser = $vuser->getField("username"); - $inUseUserRealName = $vuser->getField("realname"); - - $message = sprintf(i18n("Article is in use by %s (%s)"), $inUseUser, $inUseUserRealName); - $notification->displayNotification("warning", $message); - $inUse = true; - $disabled = 'disabled="disabled"'; - } + /* Remove all own marks */ + $col->removeSessionMarks($sess->id); - if ($tmp_locked == 1) - { - $inUse = true; - $disabled = 'disabled="disabled"'; - } + if (($obj = $col->checkMark("article", $tmp_idartlang)) === false) { + $col->markInUse("article", $tmp_idartlang, $sess->id, $auth->auth["uid"]); + $inUse = false; + $disabled = ""; + } else { - } else { + $vuser = new User; + $vuser->loadUserByUserID($obj->get("userid")); + $inUseUser = $vuser->getField("username"); + $inUseUserRealName = $vuser->getField("realname"); - //***************** this art is edited the first time ************* + $message = sprintf(i18n("Article is in use by %s (%s)"), $inUseUser, $inUseUserRealName); + $notification->displayNotification("warning", $message); + $inUse = true; + $disabled = 'disabled="disabled"'; + } - if (!$idart) $tmp_firstedit = 1; //**** is needed when input is written to db (update or insert) + if ($tmp_locked == 1) { + $inUse = true; + $disabled = 'disabled="disabled"'; + } + } else { - $tmp_idartlang = 0; - $tmp_idlang = $lang; - $tmp_page_title = stripslashes($db->f("pagetitle")); - $tmp_title = ""; - $tmp_urlname = ""; // plugin Advanced Mod Rewrite - edit by stese - $tmp_artspec = ""; - $tmp_summary = ""; - $tmp_created = date("Y-m-d H:i:s"); - $tmp_lastmodified = date("Y-m-d H:i:s"); - $tmp_published = date("Y-m-d H:i:s"); - $tmp_publishedby = ""; - $tmp_author = ""; - $tmp_online = "0"; - $tmp_datestart = "0000-00-00 00:00:00"; - $tmp_dateend = "0000-00-00 00:00:00"; - $tmp_keyart = ""; - $tmp_keyautoart = ""; - $tmp_sort = ""; + //***************** this art is edited the first time ************* - if (!strHasStartArticle($idcat, $lang)) - { - $tmp_is_start = 1; - } + if (!$idart) + $tmp_firstedit = 1; //**** is needed when input is written to db (update or insert) - $tmp_redirect_checked = ''; - $tmp_redirect_url = "http://"; - $tmp_external_redirect = ''; + $tmp_idartlang = 0; + $tmp_idlang = $lang; + $tmp_page_title = stripslashes($db->f("pagetitle")); + $tmp_title = ""; + $tmp_urlname = ""; // plugin Advanced Mod Rewrite - edit by stese + $tmp_artspec = ""; + $tmp_summary = ""; + $tmp_created = date("Y-m-d H:i:s"); + $tmp_lastmodified = date("Y-m-d H:i:s"); + $tmp_published = date("Y-m-d H:i:s"); + $tmp_publishedby = ""; + $tmp_author = ""; + $tmp_online = "0"; + $tmp_datestart = "0000-00-00 00:00:00"; + $tmp_dateend = "0000-00-00 00:00:00"; + $tmp_keyart = ""; + $tmp_keyautoart = ""; + $tmp_sort = ""; - } + if (!strHasStartArticle($idcat, $lang)) { + $tmp_is_start = 1; + } - $dateformat = getEffectiveSetting("backend", "timeformat", "Y-m-d H:i:s"); - - $tmp2_created = date($dateformat,strtotime($tmp_created)); - $tmp2_lastmodified = date($dateformat,strtotime($tmp_lastmodified)); - $tmp2_published = date($dateformat,strtotime($tmp_published)); + $tmp_redirect_checked = ''; + $tmp_redirect_url = "http://"; + $tmp_external_redirect = ''; + } - $tpl->set('s', 'ACTION', $sess->url("main.php?area=$area&frame=$frame&action=con_saveart") ); - $tpl->set('s', 'HIDDENSESSION', $sess->hidden_session(true)); - $tpl->set('s', 'TMP_FIRSTEDIT', $tmp_firstedit); - $tpl->set('s', 'IDART', $idart); - $tpl->set('s', 'SID', $sess->id); - $tpl->set('s', 'IDCAT', $idcat); - $tpl->set('s', 'IDARTLANG', $tmp_idartlang ); + $dateformat = getEffectiveSetting("backend", "timeformat", "Y-m-d H:i:s"); - $hiddenfields = ' - + $tmp2_created = date($dateformat, strtotime($tmp_created)); + $tmp2_lastmodified = date($dateformat, strtotime($tmp_lastmodified)); + $tmp2_published = date($dateformat, strtotime($tmp_published)); + + $tpl->set('s', 'ACTION', $sess->url("main.php?area=$area&frame=$frame&action=con_saveart")); + $tpl->set('s', 'HIDDENSESSION', $sess->hidden_session(true)); + $tpl->set('s', 'TMP_FIRSTEDIT', $tmp_firstedit); + $tpl->set('s', 'IDART', $idart); + $tpl->set('s', 'SID', $sess->id); + $tpl->set('s', 'IDCAT', $idcat); + $tpl->set('s', 'IDARTLANG', $tmp_idartlang); + + $hiddenfields = ' + '; - $tpl->set('s', 'HIDDENFIELDS', $hiddenfields); + $tpl->set('s', 'HIDDENFIELDS', $hiddenfields); - // Show path of selected category to user - $catString = ''; - prCreateURLNameLocationString($idcat, '/', $catString); - $tpl->set('s', 'CATEGORY', $catString.'/'.clHtmlSpecialChars($tmp_title)); - - /* Title */ - $tpl->set('s', 'TITEL', i18n("Title")); + // Show path of selected category to user + $catString = ''; + prCreateURLNameLocationString($idcat, '/', $catString); + $tpl->set('s', 'CATEGORY', $catString . '/' . clHtmlSpecialChars($tmp_title)); + + /* Title */ + $tpl->set('s', 'TITEL', i18n("Title")); // plugin Advanced Mod Rewrite - edit by stese $tpl->set('s', 'URLNAME', i18n("Alias")); // end plugin Advanced Mod Rewrite - $arrArtSpecs = getArtSpec(); + $arrArtSpecs = getArtSpec(); - $tmp_inputArtSort = ""; + $iAvariableSpec = 0; + foreach ($arrArtSpecs as $id => $value) { if ($arrArtSpecs[$id]['online'] == 1) { - if (($arrArtSpecs[$id]['default'] == 1) && (strlen($tmp_artspec) == 0 || $tmp_artspec == 0)) - { - $tmp_inputArtSort .= ""; - } elseif ($id == $tmp_artspec) - { - $tmp_inputArtSort .= ""; - } else - { - $tmp_inputArtSort .= ""; + if (($arrArtSpecs[$id]['default'] == 1) && (strlen($tmp_artspec) == 0 || $tmp_artspec == 0)) { + $tmp_inputArtSort .= ""; + } elseif ($id == $tmp_artspec) { + $tmp_inputArtSort .= ""; + } else { + $tmp_inputArtSort .= ""; } $iAvariableSpec++; } } $tmp_inputArtSort .= ""; - - if ($iAvariableSpec == 0) - { - $tmp_inputArtSort = i18n("No article specifications found!"); - } - $tpl->set('s', 'ARTIKELART', i18n("Article specification")); - $tpl->set('s', 'ARTIKELARTSELECT', $tmp_inputArtSort); + if ($iAvariableSpec == 0) { + $tmp_inputArtSort = i18n("No article specifications found!"); + } - $tpl->set('s', 'TITEL-FIELD', ''); + $tpl->set('s', 'ARTIKELART', i18n("Article specification")); + $tpl->set('s', 'ARTIKELARTSELECT', $tmp_inputArtSort); + + $tpl->set('s', 'TITEL-FIELD', ''); // plugin Advanced Mod Rewrite - edit by stese - $tpl->set('s', 'URLNAME-FIELD', ''); + $tpl->set('s', 'URLNAME-FIELD', ''); // end plugin Advanced Mod Rewrite - $tpl->set('s', 'ARTIKELID', "idart"); - $tpl->set('s', 'ARTID', $idart); + $tpl->set('s', 'ARTIKELID', "idart"); + $tpl->set('s', 'ARTID', $idart); - $tpl->set('s', 'DIRECTLINKTEXT', i18n("Articlelink")); + $tpl->set('s', 'DIRECTLINKTEXT', i18n("Articlelink")); - $select = new cHTMLSelectElement("directlink"); - $select->setEvent("change", "document.getElementById('linkhint').value = this.form.directlink.options[this.form.directlink.options.selectedIndex].value;"); + $select = new cHTMLSelectElement("directlink"); + $select->setEvent("change", "document.getElementById('linkhint').value = this.form.directlink.options[this.form.directlink.options.selectedIndex].value;"); - $baselink = $cfgClient[$client]["path"]["htmlpath"]."front_content.php?idart=$idart"; + $baselink = $cfgClient[$client]["path"]["htmlpath"] . "front_content.php?idart=$idart"; - $option[0] = new cHTMLOptionElement( i18n("Select an entry to display link"), ""); - $option[1] = new cHTMLOptionElement( i18n("Article only"), $baselink); - $option[2] = new cHTMLOptionElement( i18n("Article with Category"), $baselink."&idcat=$idcat"); - $option[3] = new cHTMLOptionElement( i18n("Article with Category and Language"), $baselink."&idcat=$idcat&lang=$lang"); - $option[4] = new cHTMLOptionElement( i18n("Article with Language"), $baselink."&lang=$lang"); + $option[0] = new cHTMLOptionElement(i18n("Select an entry to display link"), ""); + $option[1] = new cHTMLOptionElement(i18n("Article only"), $baselink); + $option[2] = new cHTMLOptionElement(i18n("Article with Category"), $baselink . "&idcat=$idcat"); + $option[3] = new cHTMLOptionElement(i18n("Article with Category and Language"), $baselink . "&idcat=$idcat&lang=$lang"); + $option[4] = new cHTMLOptionElement(i18n("Article with Language"), $baselink . "&lang=$lang"); - $select->addOptionElement(0, $option[0]); - $select->addOptionElement(1, $option[1]); - $select->addOptionElement(2, $option[2]); - $select->addOptionElement(3, $option[3]); - $select->addOptionElement(4, $option[4]); + $select->addOptionElement(0, $option[0]); + $select->addOptionElement(1, $option[1]); + $select->addOptionElement(2, $option[2]); + $select->addOptionElement(3, $option[3]); + $select->addOptionElement(4, $option[4]); - $tpl->set('s', 'DIRECTLINK', $select->render().'

'); + $tpl->set('s', 'DIRECTLINK', $select->render() . '

'); - $tpl->set('s', 'ZUORDNUNGSID', "idcatart"); - $tpl->set('s', 'ALLOCID', $tmp_cat_art); + $tpl->set('s', 'ZUORDNUNGSID', "idcatart"); + $tpl->set('s', 'ALLOCID', $tmp_cat_art); - /* Author */ - $tpl->set('s', 'AUTHOR_CREATOR', i18n("Author (Creator)")); - $tpl->set('s', 'AUTOR-ERSTELLUNGS-NAME', $classuser->getRealnameByUserName($tmp_author).''.' '); - $tpl->set('s', 'AUTOR-AENDERUNG-NAME', $classuser->getRealnameByUserName($tmp_modifiedby).' '); + /* Author */ + $tpl->set('s', 'AUTHOR_CREATOR', i18n("Author (Creator)")); + $tpl->set('s', 'AUTOR-ERSTELLUNGS-NAME', $classuser->getRealnameByUserName($tmp_author) . '' . ' '); + $tpl->set('s', 'AUTOR-AENDERUNG-NAME', $classuser->getRealnameByUserName($tmp_modifiedby) . ' '); - /* Created */ - $tmp_erstellt = ($tmp_firstedit == 1) ? '' : ''; - $tpl->set('s', 'ERSTELLT', i18n("Created")); - $tpl->set('s', 'ERSTELLUNGS-DATUM', $tmp2_created.$tmp_erstellt); + /* Created */ + $tmp_erstellt = ($tmp_firstedit == 1) ? '' : ''; + $tpl->set('s', 'ERSTELLT', i18n("Created")); + $tpl->set('s', 'ERSTELLUNGS-DATUM', $tmp2_created . $tmp_erstellt); - /* Last modified */ + /* Last modified */ $tpl->set('s', 'AUTHOR_MODIFIER', i18n("Author (Modifier)")); - $tpl->set('s', 'LETZTE-AENDERUNG', i18n("Last modified")); - $tpl->set('s', 'AENDERUNGS-DATUM', $tmp2_lastmodified.''); + $tpl->set('s', 'LETZTE-AENDERUNG', i18n("Last modified")); + $tpl->set('s', 'AENDERUNGS-DATUM', $tmp2_lastmodified . ''); - /* Published */ - $tpl->set('s', 'PUBLISHING_DATE_LABEL', i18n("Publishing date")); - if($tmp_online){ - $tpl->set('s', 'PUBLISHING_DATE', $tmp2_published); - }else{ - $tpl->set('s', 'PUBLISHING_DATE', i18n("not yet published")); - } + /* Published */ + $tpl->set('s', 'PUBLISHING_DATE_LABEL', i18n("Publishing date")); + if ($tmp_online) { + $tpl->set('s', 'PUBLISHING_DATE', $tmp2_published); + } else { + $tpl->set('s', 'PUBLISHING_DATE', i18n("not yet published")); + } - $tpl->set('s', 'PUBLISHER', i18n("Publisher")); - if($classuser->getRealnameByUserName($tmp_publishedby)!=''){ - $tpl->set('s', 'PUBLISHER_NAME', ''.$classuser->getRealnameByUserName($tmp_publishedby)); - }else{ - $tpl->set('s', 'PUBLISHER_NAME', ''.' '); - } + $tpl->set('s', 'PUBLISHER', i18n("Publisher")); + if ($classuser->getRealnameByUserName($tmp_publishedby) != '') { + $tpl->set('s', 'PUBLISHER_NAME', '' . $classuser->getRealnameByUserName($tmp_publishedby)); + } else { + $tpl->set('s', 'PUBLISHER_NAME', '' . ' '); + } - /* Redirect */ - $tpl->set('s', 'WEITERLEITUNG', i18n("Redirect")); - $tpl->set('s', 'CHECKBOX', ''); + /* Redirect */ + $tpl->set('s', 'WEITERLEITUNG', i18n("Redirect")); + $tpl->set('s', 'CHECKBOX', ''); - /* Redirect - URL */ - if ($tmp_redirect_checked != '') - { - $forceDisable = ""; - } else { - $forceDisable = "disabled"; - } + /* Redirect - URL */ + if ($tmp_redirect_checked != '') { + $forceDisable = ""; + } else { + $forceDisable = "disabled"; + } - $tpl->set('s', 'URL', ''); + $tpl->set('s', 'URL', ''); - /* Redirect - New window */ - if (getEffectiveSetting("articles", "show-new-window-checkbox", "false") == "true") - { - $tpl->set('s', 'CHECKBOX-NEWWINDOW', ''); - } else { - $tpl->set('s', 'CHECKBOX-NEWWINDOW', ' '); - } + /* Redirect - New window */ + if (getEffectiveSetting("articles", "show-new-window-checkbox", "false") == "true") { + $tpl->set('s', 'CHECKBOX-NEWWINDOW', ''); + } else { + $tpl->set('s', 'CHECKBOX-NEWWINDOW', ' '); + } - /* Online */ - if ($perm->have_perm_area_action("con", "con_makeonline") || - $perm->have_perm_area_action_item("con","con_makeonline", $idcat)) - { - $tmp_ocheck = ($tmp_online != 1) ? '' : ''; - } else { - $tmp_ocheck = ($tmp_online != 1) ? '' : ''; - } + /* Online */ + if ($perm->have_perm_area_action("con", "con_makeonline") || $perm->have_perm_area_action_item("con", "con_makeonline", $idcat)) { + $tmp_ocheck = ($tmp_online != 1) ? '' : ''; + } else { + $tmp_ocheck = ($tmp_online != 1) ? '' : ''; + } - $tpl->set('s', 'ONLINE', 'Online'); - $tpl->set('s', 'ONLINE-CHECKBOX', $tmp_ocheck); + $tpl->set('s', 'ONLINE', 'Online'); + $tpl->set('s', 'ONLINE-CHECKBOX', $tmp_ocheck); + + /* Startartikel */ + if ($perm->have_perm_area_action("con", "con_makestart") || $perm->have_perm_area_action_item("con", "con_makestart", $idcat)) { + $tmp_start = ($tmp_is_start == 0) ? '' : ''; + } else { + $tmp_start = ($tmp_is_start == 0) ? '' : ''; + } + $tpl->set('s', 'STARTARTIKEL', i18n("Start article")); + $tpl->set('s', 'STARTARTIKEL-CHECKBOX', $tmp_start); + + /* Sortierung */ + $tpl->set('s', 'SORTIERUNG', i18n("Sort key")); + $tpl->set('s', 'SORTIERUNG-FIELD', ''); + + /* Category select */ + + /* Fetch setting */ + $oClient = new cApiClient($client); + $cValue = $oClient->getProperty("system", "multiassign", true); + $sValue = getSystemProperty("system", "multiassign", true); + + $tpl2 = new Template; + $button = ""; + $moveOK = true; + + if ($cValue == false || $sValue == false) { + $sql = "SELECT idartlang, online FROM " . $cfg["tab"]["art_lang"] . " WHERE idart = '" . Contenido_Security::toInteger($idart) . "' AND online='1' AND idlang != '" . Contenido_Security::toInteger($lang) . "'"; + $db->query($sql); + + if ($db->num_rows() > 0) { + $moveOK = false; + } else { + $moveOK = true; + } + + if ($moveOK == true) { + if (count(conGetCategoryAssignments($idart)) > 1) { + /* Old behaviour */ + $tpl2 = new Template; + $tpl2->set('s', 'ID', 'catsel'); + $tpl2->set('s', 'NAME', 'fake[]'); + $tpl2->set('s', 'CLASS', 'text_medium'); + $tpl2->set('s', 'OPTIONS', 'multiple="multiple" disabled="disabled" size="14" style="width: 400px;scrollbar-face-color:#C6C6D5;scrollbar-highlight-color:#FFFFFF;scrollbar-3dlight-color:#747488;scrollbar-darkshadow-color:#000000;scrollbar-shadow-color:#334F77;scrollbar-arrow-color:#334F77;scrollbar-track-color:#C7C7D6;background:lightgrey;"'); + + $rbutton = new cHTMLButton("removeassignment", i18n("Remove assignments")); + + $boxTitle = i18n("Remove multiple category assignments"); + $boxDescr = i18n("Do you really want to remove the assignments to all categories except the current one?"); + + $rbutton->setEvent("click", 'box.confirm(\'' . $boxTitle . '\', \'' . $boxDescr . '\', \'removeAssignments(' . $idart . ', ' . $idcat . ')\'); return false;'); + $button = "
" . $rbutton->render(); + + $moveOK = false; + } else { + $tpl2 = new Template; + $tpl2->set('s', 'ID', 'catsel'); + $tpl2->set('s', 'NAME', 'idcatnew[]'); + $tpl2->set('s', 'CLASS', 'text_medium'); + $tpl2->set('s', 'OPTIONS', 'size="14" style="width: 400px;scrollbar-face-color:#C6C6D5;scrollbar-highlight-color:#FFFFFF;scrollbar-3dlight-color:#747488;scrollbar-darkshadow-color:#000000;scrollbar-shadow-color:#334F77;scrollbar-arrow-color:#334F77;scrollbar-track-color:#C7C7D6;"'); + } + } else { + + $note = i18n("Language parts of the articles are existing in other languages and are online. To change the category assignment, please set the other articles offline first."); + $tpl2->set('s', 'ID', 'catsel'); + $tpl2->set('s', 'NAME', 'fake[]'); + $tpl2->set('s', 'CLASS', 'text_medium'); + $tpl2->set('s', 'OPTIONS', 'multiple="multiple" disabled="disabled" size="14" style="width: 400px;scrollbar-face-color:#C6C6D5;scrollbar-highlight-color:#FFFFFF;scrollbar-3dlight-color:#747488;scrollbar-darkshadow-color:#000000;scrollbar-shadow-color:#334F77;scrollbar-arrow-color:#334F77;scrollbar-track-color:#C7C7D6;background:lightgrey;"'); + } + } else { + /* Old behaviour */ + $tpl2->set('s', 'ID', 'catsel'); + $tpl2->set('s', 'NAME', 'idcatnew[]'); + $tpl2->set('s', 'CLASS', 'text_medium'); + $tpl2->set('s', 'OPTIONS', 'multiple="multiple" ' . $disabled . ' size="14" style="width: 400px;scrollbar-face-color:#C6C6D5;scrollbar-highlight-color:#FFFFFF;scrollbar-3dlight-color:#747488;scrollbar-darkshadow-color:#000000;scrollbar-shadow-color:#334F77;scrollbar-arrow-color:#334F77;scrollbar-track-color:#C7C7D6;"'); + } - /* Startartikel */ - if ($perm->have_perm_area_action("con", "con_makestart") || - $perm->have_perm_area_action_item("con","con_makestart", $idcat)) - { - $tmp_start = ($tmp_is_start == 0) ? '' : ''; - } else { - $tmp_start = ($tmp_is_start == 0) ? '' : ''; - } - $tpl->set('s', 'STARTARTIKEL', i18n("Start article")); - $tpl->set('s', 'STARTARTIKEL-CHECKBOX', $tmp_start); + if (isset($tplinputchanged) && $tplinputchanged == 1) { + $tmp_idcat_in_art = $idcatnew; + } else { + $sql = "SELECT idcat FROM " . $cfg["tab"]["cat_art"] . " WHERE idart='" . $idart . "'"; // get all idcats that contain art + $db->query($sql); - /* Sortierung */ - $tpl->set('s', 'SORTIERUNG', i18n("Sort key")); - $tpl->set('s', 'SORTIERUNG-FIELD', ''); + while ($db->next_record()) { + $tmp_idcat_in_art[] = $db->f("idcat"); + } - /* Category select */ + if (!is_array($tmp_idcat_in_art)) { + $tmp_idcat_in_art[0] = $idcat; + } + } - /* Fetch setting */ - $oClient = new cApiClient($client); - $cValue = $oClient->getProperty("system", "multiassign", true); - $sValue = getSystemProperty("system", "multiassign", true); - - $tpl2 = new Template; - $button = ""; - $moveOK = true; - - if ($cValue == false || $sValue == false) - { - $sql = "SELECT idartlang, online FROM ".$cfg["tab"]["art_lang"]." WHERE idart = '".Contenido_Security::toInteger($idart)."' AND online='1' AND idlang != '".Contenido_Security::toInteger($lang)."'"; - $db->query($sql); - - if ($db->num_rows() > 0) - { - $moveOK = false; - } else { - $moveOK = true; - } - - if ($moveOK == true) - { - if (count(conGetCategoryAssignments($idart)) > 1) - { - /* Old behaviour */ - $tpl2 = new Template; - $tpl2->set('s', 'ID', 'catsel'); - $tpl2->set('s', 'NAME', 'fake[]'); - $tpl2->set('s', 'CLASS', 'text_medium'); - $tpl2->set('s', 'OPTIONS', 'multiple="multiple" disabled="disabled" size="14" style="width: 400px;scrollbar-face-color:#C6C6D5;scrollbar-highlight-color:#FFFFFF;scrollbar-3dlight-color:#747488;scrollbar-darkshadow-color:#000000;scrollbar-shadow-color:#334F77;scrollbar-arrow-color:#334F77;scrollbar-track-color:#C7C7D6;background:lightgrey;"'); - - $rbutton = new cHTMLButton("removeassignment", i18n("Remove assignments")); - - $boxTitle = i18n("Remove multiple category assignments"); - $boxDescr = i18n("Do you really want to remove the assignments to all categories except the current one?"); - - $rbutton->setEvent("click", 'box.confirm(\''.$boxTitle.'\', \''.$boxDescr.'\', \'removeAssignments('.$idart.', '.$idcat.')\'); return false;'); - $button = "
".$rbutton->render(); - - $moveOK = false; - - } else { - $tpl2 = new Template; - $tpl2->set('s', 'ID', 'catsel'); - $tpl2->set('s', 'NAME', 'idcatnew[]'); - $tpl2->set('s', 'CLASS', 'text_medium'); - $tpl2->set('s', 'OPTIONS', 'size="14" style="width: 400px;scrollbar-face-color:#C6C6D5;scrollbar-highlight-color:#FFFFFF;scrollbar-3dlight-color:#747488;scrollbar-darkshadow-color:#000000;scrollbar-shadow-color:#334F77;scrollbar-arrow-color:#334F77;scrollbar-track-color:#C7C7D6;"'); - } - } else { - - $note = i18n("Language parts of the articles are existing in other languages and are online. To change the category assignment, please set the other articles offline first."); - $tpl2->set('s', 'ID', 'catsel'); - $tpl2->set('s', 'NAME', 'fake[]'); - $tpl2->set('s', 'CLASS', 'text_medium'); - $tpl2->set('s', 'OPTIONS', 'multiple="multiple" disabled="disabled" size="14" style="width: 400px;scrollbar-face-color:#C6C6D5;scrollbar-highlight-color:#FFFFFF;scrollbar-3dlight-color:#747488;scrollbar-darkshadow-color:#000000;scrollbar-shadow-color:#334F77;scrollbar-arrow-color:#334F77;scrollbar-track-color:#C7C7D6;background:lightgrey;"'); - } - - - } else { - /* Old behaviour */ - $tpl2->set('s', 'ID', 'catsel'); - $tpl2->set('s', 'NAME', 'idcatnew[]'); - $tpl2->set('s', 'CLASS', 'text_medium'); - $tpl2->set('s', 'OPTIONS', 'multiple="multiple" '.$disabled.' size="14" style="width: 400px;scrollbar-face-color:#C6C6D5;scrollbar-highlight-color:#FFFFFF;scrollbar-3dlight-color:#747488;scrollbar-darkshadow-color:#000000;scrollbar-shadow-color:#334F77;scrollbar-arrow-color:#334F77;scrollbar-track-color:#C7C7D6;"'); - } + /* Start date */ + if ($tmp_datestart == "0000-00-00 00:00:00") { + $tpl->set('s', 'STARTDATE', ''); + } else { + $tpl->set('s', 'STARTDATE', $tmp_datestart); + } - if ( isset($tplinputchanged) && $tplinputchanged == 1 ) { - $tmp_idcat_in_art = $idcatnew; + /* End date */ + if ($tmp_dateend == "0000-00-00 00:00:00") { + $tpl->set('s', 'ENDDATE', ''); + } else { + $tpl->set('s', 'ENDDATE', $tmp_dateend); + } - } else { - $sql = "SELECT idcat FROM ".$cfg["tab"]["cat_art"]." WHERE idart='".$idart."'"; // get all idcats that contain art - $db->query($sql); - - while ( $db->next_record() ) { - $tmp_idcat_in_art[] = $db->f("idcat"); - } - - if (!is_array($tmp_idcat_in_art)) { - $tmp_idcat_in_art[0] = $idcat; - } - } - - /* Start date */ - if ($tmp_datestart == "0000-00-00 00:00:00") - { - $tpl->set('s', 'STARTDATE', ''); - } else { - $tpl->set('s', 'STARTDATE', $tmp_datestart); - } - - - /* End date */ - if ($tmp_dateend == "0000-00-00 00:00:00") - { - $tpl->set('s', 'ENDDATE',''); - } else { - $tpl->set('s', 'ENDDATE', $tmp_dateend); - } - - $sql = "SELECT + $sql = "SELECT A.idcat, A.level, C.name FROM - ".$cfg["tab"]["cat_tree"]." AS A, - ".$cfg["tab"]["cat"]." AS B, - ".$cfg["tab"]["cat_lang"]." AS C + " . $cfg["tab"]["cat_tree"] . " AS A, + " . $cfg["tab"]["cat"] . " AS B, + " . $cfg["tab"]["cat_lang"] . " AS C WHERE A.idcat=B.idcat AND B.idcat=C.idcat AND - C.idlang='".Contenido_Security::toInteger($lang)."' AND - B.idclient='".Contenido_Security::toInteger($client)."' + C.idlang='" . Contenido_Security::toInteger($lang) . "' AND + B.idclient='" . Contenido_Security::toInteger($client) . "' ORDER BY A.idtree"; - $db->query($sql); + $db->query($sql); - while ( $db->next_record() ) { + while ($db->next_record()) { - $spaces = ""; + $spaces = ""; - for ($i = 0; $i < $db->f("level"); $i ++) { - $spaces .= "    "; - } + for ($i = 0; $i < $db->f("level"); $i++) { + $spaces .= "    "; + } - if ( !in_array($db->f("idcat"), $tmp_idcat_in_art) ) { - $tpl2->set('d', 'VALUE', $db->f("idcat")); - $tpl2->set('d', 'SELECTED', ''); - $tpl2->set('d', 'CAPTION', $spaces.Contenido_Security::unFilter($db->f("name"))); + if (!in_array($db->f("idcat"), $tmp_idcat_in_art)) { + $tpl2->set('d', 'VALUE', $db->f("idcat")); + $tpl2->set('d', 'SELECTED', ''); + $tpl2->set('d', 'CAPTION', $spaces . Contenido_Security::unFilter($db->f("name"))); - $tpl2->next(); + $tpl2->next(); + } else { + $tpl2->set('d', 'VALUE', $db->f("idcat")); + $tpl2->set('d', 'SELECTED', 'selected="selected"'); + $tpl2->set('d', 'CAPTION', $spaces . Contenido_Security::unFilter($db->f("name"))); + $tpl2->next(); - } else { - $tpl2->set('d', 'VALUE', $db->f("idcat")); - $tpl2->set('d', 'SELECTED', 'selected="selected"'); - $tpl2->set('d', 'CAPTION', $spaces.Contenido_Security::unFilter($db->f("name"))); - $tpl2->next(); + if ($moveOK == false) { + $button .= ''; + } + } + } - if ($moveOK == false) - { - $button .= ''; - } + $select = $tpl2->generate($cfg["path"]["templates"] . $cfg["templates"]["generic_select"], true); - } - } + /* Struktur */ + $tpl->set('s', 'STRUKTUR', i18n("Category")); + $tpl->set('s', 'STRUKTUR-FIELD', $select . $button); - $select = $tpl2->generate($cfg["path"]["templates"] . $cfg["templates"]["generic_select"], true); + if (isset($tmp_notification)) { + $tpl->set('s', 'NOTIFICATION', '' . $tmp_notification . '
'); + } else { + $tpl->set('s', 'NOTIFICATION', ''); + } - /* Struktur */ - $tpl->set('s', 'STRUKTUR', i18n("Category")); - $tpl->set('s', 'STRUKTUR-FIELD', $select . $button); - - if (isset($tmp_notification)) { - $tpl->set('s', 'NOTIFICATION', ''.$tmp_notification.'
'); - } else { - $tpl->set('s', 'NOTIFICATION', ''); - } - - if (($perm->have_perm_area_action("con", "con_makeonline") || - $perm->have_perm_area_action_item("con","con_makeonline", $idcat)) && $inUse == false) - { - $allow_usetimemgmt = ''; - $sCalStartInit = ''; - - $sCalEndInit = ''; - $tpl->set('s', 'CHOOSEEND', ''.i18n('.$sCalEndInit); - $tpl->set('s', 'CHOOSESTART', ''.i18n('.$sCalStartInit); - } else { - $allow_usetimemgmt = ' disabled="disabled"'; - $tpl->set('s', 'CHOOSEEND', ''); - $tpl->set('s', 'CHOOSESTART', ''); - } + $tpl->set('s', 'CHOOSEEND', '' . i18n(' . $sCalEndInit); + $tpl->set('s', 'CHOOSESTART', '' . i18n(' . $sCalStartInit); + } else { + $allow_usetimemgmt = ' disabled="disabled"'; + $tpl->set('s', 'CHOOSEEND', ''); + $tpl->set('s', 'CHOOSESTART', ''); + } - $tpl->set('s', 'SDOPTS', $allow_usetimemgmt); - $tpl->set('s', 'EDOPTS', $allow_usetimemgmt); + $tpl->set('s', 'SDOPTS', $allow_usetimemgmt); + $tpl->set('s', 'EDOPTS', $allow_usetimemgmt); - if ($tmp_usetimemgmt == '1') - { - $tpl->set('s','TIMEMGMTCHECKED', 'checked'.$allow_usetimemgmt); - } else { - $tpl->set('s', 'TIMEMGMTCHECKED', $allow_usetimemgmt); - } + if ($tmp_usetimemgmt == '1') { + $tpl->set('s', 'TIMEMGMTCHECKED', 'checked' . $allow_usetimemgmt); + } else { + $tpl->set('s', 'TIMEMGMTCHECKED', $allow_usetimemgmt); + } - unset ($tpl2); - /* Nach Kategorie Verschieben */ - $tpl2 = new Template; - $tpl2->set('s', 'ID', 'catsel'); - $tpl2->set('s', 'NAME', 'time_target_cat'); - $tpl2->set('s', 'CLASS', 'text_medium'); - $tpl2->set('s', 'OPTIONS', 'size="1" style="width: 160px;scrollbar-face-color:#C6C6D5;scrollbar-highlight-color:#FFFFFF;scrollbar-3dlight-color:#B3B3B3;scrollbar-darkshadow-color:#000000;scrollbar-shadow-color:#334F77;scrollbar-arrow-color:#334F77;scrollbar-track-color:#C7C7D6;"'.$allow_usetimemgmt); + unset($tpl2); + /* Nach Kategorie Verschieben */ + $tpl2 = new Template; + $tpl2->set('s', 'ID', 'catsel'); + $tpl2->set('s', 'NAME', 'time_target_cat'); + $tpl2->set('s', 'CLASS', 'text_medium'); + $tpl2->set('s', 'OPTIONS', 'size="1" style="width: 160px;scrollbar-face-color:#C6C6D5;scrollbar-highlight-color:#FFFFFF;scrollbar-3dlight-color:#B3B3B3;scrollbar-darkshadow-color:#000000;scrollbar-shadow-color:#334F77;scrollbar-arrow-color:#334F77;scrollbar-track-color:#C7C7D6;"' . $allow_usetimemgmt); - $sql = "SELECT + $sql = "SELECT A.idcat, A.level, C.name FROM - ".$cfg["tab"]["cat_tree"]." AS A, - ".$cfg["tab"]["cat"]." AS B, - ".$cfg["tab"]["cat_lang"]." AS C + " . $cfg["tab"]["cat_tree"] . " AS A, + " . $cfg["tab"]["cat"] . " AS B, + " . $cfg["tab"]["cat_lang"] . " AS C WHERE A.idcat=B.idcat AND B.idcat=C.idcat AND - C.idlang='".Contenido_Security::toInteger($lang)."' AND - B.idclient='".Contenido_Security::toInteger($client)."' + C.idlang='" . Contenido_Security::toInteger($lang) . "' AND + B.idclient='" . Contenido_Security::toInteger($client) . "' ORDER BY A.idtree"; - $db->query($sql); + $db->query($sql); - while ( $db->next_record() ) { + while ($db->next_record()) { - $spaces = ""; + $spaces = ""; - for ($i = 0; $i < $db->f("level"); $i ++) { - $spaces .= "  "; - } + for ($i = 0; $i < $db->f("level"); $i++) { + $spaces .= "  "; + } - if ( $db->f("idcat") != $tmp_targetcat) { - $tpl2->set('d', 'VALUE', $db->f("idcat")); - $tpl2->set('d', 'SELECTED', ''); - $tpl2->set('d', 'CAPTION', $spaces.Contenido_Security::unFilter($db->f("name"))); + if ($db->f("idcat") != $tmp_targetcat) { + $tpl2->set('d', 'VALUE', $db->f("idcat")); + $tpl2->set('d', 'SELECTED', ''); + $tpl2->set('d', 'CAPTION', $spaces . Contenido_Security::unFilter($db->f("name"))); - $tpl2->next(); + $tpl2->next(); + } else { + $tpl2->set('d', 'VALUE', $db->f("idcat")); + $tpl2->set('d', 'SELECTED', 'selected="selected"'); + $tpl2->set('d', 'CAPTION', $spaces . Contenido_Security::unFilter($db->f("name"))); + $tpl2->next(); + } + } - } else { - $tpl2->set('d', 'VALUE', $db->f("idcat")); - $tpl2->set('d', 'SELECTED', 'selected="selected"'); - $tpl2->set('d', 'CAPTION', $spaces.Contenido_Security::unFilter($db->f("name"))); - $tpl2->next(); + $select = $tpl2->generate($cfg["path"]["templates"] . $cfg["templates"]["generic_select"], true); - } - } + /* Seitentitel */ + $title_input = ''; + $tpl->set("s", "TITLE-INPUT", $title_input); - $select = $tpl2->generate($cfg["path"]["templates"] . $cfg["templates"]["generic_select"], true); + /* Meta-Tags */ + $availableTags = conGetAvailableMetaTagTypes(); - /* Seitentitel */ - $title_input = ''; - $tpl->set("s", "TITLE-INPUT", $title_input); - - /* Meta-Tags */ - $availableTags = conGetAvailableMetaTagTypes(); - - $sMetaDate = ''; - - foreach ($availableTags as $key => $value) - { - $tpl->set('d', 'METAINPUT', 'META'.$value); + '; - switch ($value["fieldtype"]) - { - case "text": + foreach ($availableTags as $key => $value) { + $tpl->set('d', 'METAINPUT', 'META' . $value); + + switch ($value["fieldtype"]) { + case "text": if ($value["name"] == 'date') { - $element = ' - '.i18n('.$sMetaDate; + $element = ' + ' . i18n(' . $sMetaDate; } else { - $element = ''; - } + $element = ''; + } break; - case "textarea": - $element = ''; - break; - } - - - - $tpl->set('d', 'METAFIELDTYPE', $element); - //$tpl->set('d', 'METAVALUE', conGetMetaValue($tmp_idartlang,$key)); - $tpl->set('d', 'METATITLE', $value["name"].':'); - $tpl->next(); - } - - /* Struktur */ - $tpl->set('s', 'MOVETOCATEGORYSELECT', $select); + case "textarea": + $element = ''; + break; + } - if ($tmp_movetocat == "1") - { - $tpl->set('s','MOVETOCATCHECKED', 'checked'.$allow_usetimemgmt); - } else { - $tpl->set('s','MOVETOCATCHECKED', ''.$allow_usetimemgmt); - } - if ($tmp_onlineaftermove == "1") - { - $tpl->set('s', 'ONLINEAFTERMOVECHECKED', 'checked'.$allow_usetimemgmt); - } else { - $tpl->set('s', 'ONLINEAFTERMOVECHECKED', ''.$allow_usetimemgmt); - } + $tpl->set('d', 'METAFIELDTYPE', $element); + //$tpl->set('d', 'METAVALUE', conGetMetaValue($tmp_idartlang,$key)); + $tpl->set('d', 'METATITLE', $value["name"] . ':'); + $tpl->next(); + } - /* Summary */ - $tpl->set('s', 'SUMMARY', i18n("Summary")); - $tpl->set('s', 'SUMMARY-INPUT', ''); + /* Struktur */ + $tpl->set('s', 'MOVETOCATEGORYSELECT', $select); - $sql = "SELECT + if ($tmp_movetocat == "1") { + $tpl->set('s', 'MOVETOCATCHECKED', 'checked' . $allow_usetimemgmt); + } else { + $tpl->set('s', 'MOVETOCATCHECKED', '' . $allow_usetimemgmt); + } + + if ($tmp_onlineaftermove == "1") { + $tpl->set('s', 'ONLINEAFTERMOVECHECKED', 'checked' . $allow_usetimemgmt); + } else { + $tpl->set('s', 'ONLINEAFTERMOVECHECKED', '' . $allow_usetimemgmt); + } + + /* Summary */ + $tpl->set('s', 'SUMMARY', i18n("Summary")); + $tpl->set('s', 'SUMMARY-INPUT', ''); + + $sql = "SELECT b.idcat FROM - ".$cfg["tab"]["cat"]." AS a, - ".$cfg["tab"]["cat_lang"]." AS b, - ".$cfg["tab"]["cat_art"]." AS c + " . $cfg["tab"]["cat"] . " AS a, + " . $cfg["tab"]["cat_lang"] . " AS b, + " . $cfg["tab"]["cat_art"] . " AS c WHERE - a.idclient = '".Contenido_Security::toInteger($client)."' AND + a.idclient = '" . Contenido_Security::toInteger($client) . "' AND a.idcat = b.idcat AND c.idcat = b.idcat AND - c.idart = '".Contenido_Security::toInteger($idart)."'"; + c.idart = '" . Contenido_Security::toInteger($idart) . "'"; - $db->query($sql); - $db->next_record(); + $db->query($sql); + $db->next_record(); - $midcat = $db->f("idcat"); + $midcat = $db->f("idcat"); - if ( isset($idart) ) { + if (isset($idart)) { - if ( !isset($idartlang) || 0 == $idartlang ) { - $sql = "SELECT idartlang FROM ".$cfg["tab"]["art_lang"]." WHERE idart = '".Contenido_Security::toInteger($idart)."' AND idlang = '".Contenido_Security::toInteger($lang)."'"; - $db->query($sql); - $db->next_record(); - $idartlang = $db->f("idartlang"); - } + if (!isset($idartlang) || 0 == $idartlang) { + $sql = "SELECT idartlang FROM " . $cfg["tab"]["art_lang"] . " WHERE idart = '" . Contenido_Security::toInteger($idart) . "' AND idlang = '" . Contenido_Security::toInteger($lang) . "'"; + $db->query($sql); + $db->next_record(); + $idartlang = $db->f("idartlang"); + } + } - } + if (isset($midcat)) { - if ( isset($midcat) ) { + if (!isset($idcatlang) || 0 == $idcatlang) { + $sql = "SELECT idcatlang FROM " . $cfg["tab"]["cat_lang"] . " WHERE idcat = '" . Contenido_Security::toInteger($midcat) . "' AND idlang = '" . Contenido_Security::toInteger($lang) . "'"; + $db->query($sql); + $db->next_record(); + $idcatlang = $db->f("idcatlang"); + } + } - if ( !isset($idcatlang) || 0 == $idcatlang ) { - $sql = "SELECT idcatlang FROM ".$cfg["tab"]["cat_lang"]." WHERE idcat = '".Contenido_Security::toInteger($midcat)."' AND idlang = '".Contenido_Security::toInteger($lang)."'"; - $db->query($sql); - $db->next_record(); - $idcatlang = $db->f("idcatlang"); - } + if (isset($midcat) && isset($idart)) { - } + if (!isset($idcatart) || 0 == $idcatart) { + $sql = "SELECT idcatart FROM " . $cfg["tab"]["cat_art"] . " WHERE idart = '" . Contenido_Security::toInteger($idart) . "' AND idcat = '" . Contenido_Security::toInteger($midcat) . "'"; + $db->query($sql); + $db->next_record(); + $idcatart = $db->f("idcatart"); + } + } - if ( isset($midcat) && isset($idart) ) { - - if ( !isset($idcatart) || 0 == $idcatart ) { - $sql = "SELECT idcatart FROM ".$cfg["tab"]["cat_art"]." WHERE idart = '".Contenido_Security::toInteger($idart)."' AND idcat = '".Contenido_Security::toInteger($midcat)."'"; - $db->query($sql); - $db->next_record(); - $idcatart = $db->f("idcatart"); - } - - } - - if ( 0 != $idart && 0 != $midcat ) { - $script = 'artObj.setProperties("'.$idart.'", "'.$idartlang.'", "'.$midcat.'", "'.$idcatlang.'", "'.$idcatart.'", "'.$lang.'");'; + if (0 != $idart && 0 != $midcat) { + $script = 'artObj.setProperties("' . $idart . '", "' . $idartlang . '", "' . $midcat . '", "' . $idcatlang . '", "' . $idcatart . '", "' . $lang . '");'; } else { - $script = 'artObj.reset();'; - } + $script = 'artObj.reset();'; + } - $tpl->set('s', 'DATAPUSH', $script); + $tpl->set('s', 'DATAPUSH', $script); - $tpl->set('s', 'BUTTONDISABLE', $disabled); - - if ($inUse == true) - { - $tpl->set('s', 'BUTTONIMAGE', 'but_ok_off.gif'); - } else { - $tpl->set('s', 'BUTTONIMAGE', 'but_ok.gif'); - } + $tpl->set('s', 'BUTTONDISABLE', $disabled); - $tpl->set('s', 'CAL_LANG', substr(strtolower($belang), 0, 2)); + if ($inUse == true) { + $tpl->set('s', 'BUTTONIMAGE', 'but_ok_off.gif'); + } else { + $tpl->set('s', 'BUTTONIMAGE', 'but_ok.gif'); + } - /* Genereate the Template */ - $tpl->generate($cfg['path']['templates'] . $cfg['templates']['con_edit_form']); + $tpl->set('s', 'CAL_LANG', substr(strtolower($belang), 0, 2)); - } else { + /* Genereate the Template */ + $tpl->generate($cfg['path']['templates'] . $cfg['templates']['con_edit_form']); + } else { - /* User hat no permission - to see this form */ - $notification->displayNotification("error", i18n("Permission denied")); - - } + /* User hat no permission + to see this form */ + $notification->displayNotification("error", i18n("Permission denied")); + } } ?> diff --git a/conlite/includes/include.html_tpl_edit_form.php b/conlite/includes/include.html_tpl_edit_form.php index fd878b5..15b78ff 100644 --- a/conlite/includes/include.html_tpl_edit_form.php +++ b/conlite/includes/include.html_tpl_edit_form.php @@ -1,4 +1,5 @@ reset(); -if (!$perm->have_perm_area_action($area, $action)) -{ +if (!$perm->have_perm_area_action($area, $action)) { $notification->displayNotification("error", i18n("Permission denied")); } else if (!(int) $client > 0) { - #if there is no client selected, display empty page - $page->render(); + #if there is no client selected, display empty page + $page->render(); } else { $path = $cfgClient[$client]["tpl"]["path"]; - $sTempFilename = stripslashes($_REQUEST['tmp_file']); + $sTempFilename = stripslashes($_REQUEST['tmp_file']); $sOrigFileName = $sTempFilename; - - if (getFileType($_REQUEST['file']) != $sFileType AND strlen(stripslashes(trim($_REQUEST['file']))) > 0) - { - $sFilename .= stripslashes($_REQUEST['file']).".$sFileType"; - } else - { - $sFilename .= stripslashes($_REQUEST['file']); + + if (getFileType($_REQUEST['file']) != $sFileType AND strlen(stripslashes(trim($_REQUEST['file']))) > 0) { + $sFilename .= stripslashes($_REQUEST['file']) . ".$sFileType"; + } else { + $sFilename .= stripslashes($_REQUEST['file']); } - + if (stripslashes($_REQUEST['file'])) { $sReloadScript = ""; } else { @@ -78,121 +75,106 @@ if (!$perm->have_perm_area_action($area, $action)) } // Content Type is template - $sTypeContent = "templates"; - $aFileInfo = getFileInformation ($client, $sTempFilename, $sTypeContent, $db); - - # create new file - if ( $_REQUEST['action'] == $sActionCreate AND $_REQUEST['status'] == 'send') - { - $sTempFilename = $sFilename; - createFile($sFilename, $path); - $bEdit = fileEdit($sFilename, $_REQUEST['code'], $path); + $sTypeContent = "templates"; + $aFileInfo = getFileInformation($client, $sTempFilename, $sTypeContent, $db); + + # create new file + if ($_REQUEST['action'] == $sActionCreate AND $_REQUEST['status'] == 'send') { + $sTempFilename = $sFilename; + createFile($sFilename, $path); + $bEdit = fileEdit($sFilename, $_REQUEST['code'], $path); $sReloadScript .= ""; updateFileInformation($client, $sFilename, 'templates', $auth->auth['uid'], $_REQUEST['description'], $db); } - # edit selected file - if ( $_REQUEST['action'] == $sActionEdit AND $_REQUEST['status'] == 'send') - { - if ($sFilename != $sTempFilename) - { - $sTempFilename = renameFile($sTempFilename, $sFilename, $path); + # edit selected file + if ($_REQUEST['action'] == $sActionEdit AND $_REQUEST['status'] == 'send') { + if ($sFilename != $sTempFilename) { + $sTempFilename = renameFile($sTempFilename, $sFilename, $path); $sReloadScript .= ""; - } else - { - $sTempFilename = $sFilename; - } - - updateFileInformation($client, $sOrigFileName, 'templates', $auth->auth['uid'], $_REQUEST['description'], $db, $sFilename); - - /** - * START TRACK VERSION - **/ - $sTypeContent = "templates"; - - if((count($aFileInfo) == 0) || ((int)$aFileInfo["idsfi"] == 0)) { - $aFileInfo = getFileInformation ($client, $sTempFilename, $sTypeContent, $db); + } else { + $sTempFilename = $sFilename; + } + + updateFileInformation($client, $sOrigFileName, 'templates', $auth->auth['uid'], $_REQUEST['description'], $db, $sFilename); + + /** + * START TRACK VERSION + * */ + $sTypeContent = "templates"; + + if ((count($aFileInfo) == 0) || ((int) $aFileInfo["idsfi"] == 0)) { + $aFileInfo = getFileInformation($client, $sTempFilename, $sTypeContent, $db); $aFileInfo['description'] = ''; } - - if((count($aFileInfo) > 0) && ($aFileInfo["idsfi"] !="")) { - $oVersion = new VersionFile($aFileInfo["idsfi"], $aFileInfo, $sFilename, $sTypeContent, $cfg, $cfgClient, $db, $client, $area, $frame, $sOrigFileName); - // Create new Layout Version in cms/version/css/ folder - $oVersion->createNewVersion(); - } - /** - * END TRACK VERSION - **/ - - $bEdit = fileEdit($sFilename, $_REQUEST['code'], $path); - } - - # generate edit form - if (isset($_REQUEST['action'])) - { - $sAction = ($bEdit) ? $sActionEdit : $_REQUEST['action']; - - if ($_REQUEST['action'] == $sActionEdit) - { - $sCode = getFileContent($sFilename, $path); - } else - { - $sCode = stripslashes($_REQUEST['code']); # stripslashes is required here in case of creating a new file - } - - /* Try to validate html */ - if (getEffectiveSetting("layout", "htmlvalidator", "true") == "true" && $sCode !== "") - { - $v = new cHTMLValidator; - $v->validate($sCode); - $msg = ""; + if ((count($aFileInfo) > 0) && ($aFileInfo["idsfi"] != "")) { + $oVersion = new VersionFile($aFileInfo["idsfi"], $aFileInfo, $sFilename, $sTypeContent, $cfg, $cfgClient, $db, $client, $area, $frame, $sOrigFileName); + // Create new Layout Version in cms/version/css/ folder + $oVersion->createNewVersion(); + } + /** + * END TRACK VERSION + * */ + $bEdit = fileEdit($sFilename, $_REQUEST['code'], $path); + } - foreach ($v->missingNodes as $value) - { - $idqualifier = ""; + # generate edit form + if (isset($_REQUEST['action'])) { + $sAction = ($bEdit) ? $sActionEdit : $_REQUEST['action']; + + if ($_REQUEST['action'] == $sActionEdit) { + $sCode = getFileContent($sFilename, $path); + } else { + $sCode = $_REQUEST['code']; # stripslashes is required here in case of creating a new file + } + + /* Try to validate html */ + if (getEffectiveSetting("layout", "htmlvalidator", "true") == "true" && $sCode !== "") { + $v = new cHTMLValidator; + $v->validate($sCode); + $msg = ""; + + foreach ($v->missingNodes as $value) { + $idqualifier = ""; + + $attr = array(); + + if ($value["name"] != "") { + $attr["name"] = "name '" . $value["name"] . "'"; + } + + if ($value["id"] != "") { + $attr["id"] = "id '" . $value["id"] . "'"; + } + + $idqualifier = implode(", ", $attr); + + if ($idqualifier != "") { + $idqualifier = "($idqualifier)"; + } + $msg .= sprintf(i18n("Tag '%s' %s has no end tag (start tag is on line %s char %s)"), $value["tag"], $idqualifier, $value["line"], $value["char"]) . "
"; + } + + if ($msg != "") { + $notis = $notification->returnNotification("warning", $msg) . "
"; + } + } - $attr = array(); - - if ($value["name"] != "") - { - $attr["name"] = "name '".$value["name"]."'"; - } - - if ($value["id"] != "") - { - $attr["id"] = "id '".$value["id"]."'"; - } - - $idqualifier = implode(", ",$attr); - - if ($idqualifier != "") - { - $idqualifier = "($idqualifier)"; - } - $msg .= sprintf(i18n("Tag '%s' %s has no end tag (start tag is on line %s char %s)"), $value["tag"], $idqualifier, $value["line"],$value["char"]) . "
"; - } - - if ($msg != "") - { - $notis = $notification->returnNotification("warning", $msg) . "
"; - } - } - $aFileInfo = getFileInformation($client, $sTempFilename, $sTypeContent, $db); - + $form = new UI_Table_Form("file_editor"); $form->addHeader(i18n("Edit file")); $form->setWidth("100%"); @@ -201,28 +183,26 @@ if (!$perm->have_perm_area_action($area, $action)) $form->setVar("frame", $frame); $form->setVar("status", 'send'); $form->setVar("tmp_file", $sTempFilename); - + $tb_name = new cHTMLTextbox("file", $sFilename, 60); $ta_code = new cHTMLTextarea("code", clHtmlSpecialChars($sCode), 100, 35, "code"); - $descr = new cHTMLTextarea("description", clHtmlSpecialChars($aFileInfo["description"]), 100, 5); - + $descr = new cHTMLTextarea("description", clHtmlSpecialChars($aFileInfo["description"]), 100, 5); + $ta_code->setStyle("font-family: monospace;width: 100%;"); $descr->setStyle("font-family: monospace;width: 100%;"); $ta_code->updateAttributes(array("wrap" => getEffectiveSetting('html_editor', 'wrap', 'off'))); - - $form->add(i18n("Name"),$tb_name); + + $form->add(i18n("Name"), $tb_name); $form->add(i18n("Description"), $descr->render()); - $form->add(i18n("Code"),$ta_code); - + $form->add(i18n("Code"), $ta_code); + $page->setContent($notis . $form->render()); - + $oEditArea = new EditArea('code', 'html', substr(strtolower($belang), 0, 2), true, $cfg); $page->addScript('editarea', $oEditArea->renderScript()); - - $page->addScript('reload', $sReloadScript); - $page->render(); - } - + $page->addScript('reload', $sReloadScript); + $page->render(); + } } ?> \ No newline at end of file diff --git a/conlite/includes/include.js_edit_form.php b/conlite/includes/include.js_edit_form.php index 5ea649d..3c663b1 100644 --- a/conlite/includes/include.js_edit_form.php +++ b/conlite/includes/include.js_edit_form.php @@ -140,7 +140,7 @@ if (!$perm->have_perm_area_action($area, $action)) { if ($_REQUEST['action'] == $sActionEdit) { $sCode = getFileContent($sFilename, $path); } else { - $sCode = stripslashes($_REQUEST['code']); # stripslashes is required here in case of creating a new file + $sCode = $_REQUEST['code']; # stripslashes is required here in case of creating a new file } $form = new UI_Table_Form("file_editor"); diff --git a/conlite/includes/include.js_files_overview.php b/conlite/includes/include.js_files_overview.php index c4febdc..e13505c 100644 --- a/conlite/includes/include.js_files_overview.php +++ b/conlite/includes/include.js_files_overview.php @@ -1,161 +1,120 @@ - * @license http://www.contenido.org/license/LIZENZ.txt - * @link http://www.4fb.de - * @link http://www.contenido.org - * @since file available since contenido release <= 4.6 - * - * {@internal - * created 2003-04-20 - * modified 2008-06-27, Frederic Schneider, add security fix - * - * $Id$: - * }} * */ - -if(!defined('CON_FRAMEWORK')) { - die('Illegal call'); +if (!defined('CON_FRAMEWORK')) { + die('Illegal call'); } -cInclude("includes", "functions.file.php"); +class clFileList { -$tpl->reset(); + protected $_sPath; + protected $_mFileExt; + protected $_iScanDepth; + protected $_aDirItems; + protected $_bWritable; + protected $_oTpl; -if (!(int) $client > 0) { - #if there is no client selected, display empty page - $oPage = new cPage; - $oPage->render(); - return; -} - -$path = $cfgClient[$client]["js"]["path"]; -$sFileType = "js"; - -$sSession = $sess->id; - -$sArea = 'js'; -$sActionDelete = 'js_delete'; -$sActionEdit = 'js_edit'; - -$sScriptTemplate = ' - - - -'; - -$tpl->set('s', 'JAVASCRIPT', $sScriptTemplate); - -# delete file -if ($action == $sActionDelete) -{ - if (!strrchr($_REQUEST['delfile'], "/")) - { - if (file_exists($path.$_REQUEST['delfile'])) - { - unlink($path.$_REQUEST['delfile']); - removeFileInformation($client, $_REQUEST['delfile'], 'js', $db); + public function __construct($sPath, $mFileExt = null) { + + if(!empty($mFileExt) && is_string($mFileExt)) { + $mFileExt = [strtolower($mFileExt)]; } + $this->_sPath = $sPath; + $this->_mFileExt = $mFileExt; + $this->_iScanDepth = 3; + $this->_oTpl = new Template(); } -} - -if ($handle = opendir($path)) -{ - - $aFiles = array(); - - while ($file = readdir($handle)) - { - if(substr($file, (strlen($file) - (strlen($sFileType) + 1)), (strlen($sFileType) + 1)) == ".$sFileType" AND is_readable($path.$file)) - { - $aFiles[] = $file; - }elseif (substr($file, (strlen($file) - (strlen($sFileType) + 1)), (strlen($sFileType) + 1)) == ".$sFileType" AND !is_readable($path.$file)) - { - $notification->displayNotification("error", $file." ".i18n("is not readable!")); + public function scanDir() { + if (empty($this->_sPath) || !is_readable($this->_sPath)) { + return false; } - } - closedir($handle); - - // display files - if (is_array($aFiles)) - { - - sort($aFiles); - - foreach ($aFiles as $filename) - { - - $bgcolor = ( is_int($tpl->dyn_cnt / 2) ) ? $cfg["color"]["table_light"] : $cfg["color"]["table_dark"]; - $tpl->set('d', 'BGCOLOR', $bgcolor); - - $tmp_mstr = '%s'; - - $html_filename = sprintf($tmp_mstr, 'right_top', - $sess->url("main.php?area=$area&frame=3&file=$filename"), - 'right_bottom', - $sess->url("main.php?area=$area&frame=4&action=$sActionEdit&file=$filename&tmp_file=$filename"), - $filename, $filename, clHtmlSpecialChars($filename)); + $this->_bWritable = (!is_writable($this->_sPath)) ? true : false; - $tpl->set('d', 'FILENAME', $html_filename); - - $delTitle = i18n("Delete File"); - $delDescr = sprintf(i18n("Do you really want to delete the following file:

%s
"),$filename); - - if ($perm->have_perm_area_action('style', $sActionDelete)) - { - $tpl->set('d', 'DELETE', ''); - }else - { - $tpl->set('d', 'DELETE', ''); - } - - if (stripslashes($_REQUEST['file']) == $filename) { - $tpl->set('d', 'ID', 'id="marked"'); + $this->_aDirItems = $this->_assetsMap($this->_sPath, $this->_iScanDepth); + asort($this->_aDirItems, SORT_STRING | SORT_FLAG_CASE | SORT_NATURAL); + } + + public function renderList($sTpl = null, $bReturn = false) { + global $sess, $area; + $sList = ''."\n"; + $this->_oTpl->set('s', 'item_list', $sList); + + $this->_oTpl->set('s', 'multilink1', $sess->url("main.php?area=$area&frame=3&file=\${file}")); + $this->_oTpl->set('s', 'multilink2', $sess->url("main.php?area=$area&frame=4&action=js_edit&file=\${file}&tmp_file=\${file}")); + + $this->_oTpl->generate(cRegistry::getConfigValue('path', 'contenido') . cRegistry::getConfigValue('path', 'templates') . "html5/file_list.html", $bReturn); } -}else -{ - if ((int) $client > 0) { - $notification->displayNotification("error", i18n("Directory is not existing or readable!")."
$path"); - } + + protected function _getSubItems($sName, $aItems, $sPathToItem) { + $sPathToItem = $sPathToItem.$sName.DIRECTORY_SEPARATOR; + $sItemListEntry = '
  • '.$sName."\n\t"; + if(is_array($aItems) && count($aItems) >0) { + $sItemListEntry .= ''."\n"; + } + $sItemListEntry .= '
  • '."\n"; + return $sItemListEntry; + } + + protected function _assetsMap($source_dir, $directory_depth = 0, $hidden = false) { + if ($fp = @opendir($source_dir)) { + $filedata = array(); + $new_depth = $directory_depth - 1; + $source_dir = rtrim($source_dir, '/') . '/'; + + while (FALSE !== ($file = readdir($fp))) { + // Remove '.', '..', and hidden files [optional] + if (!trim($file, '.') OR ($hidden == false && $file[0] == '.')) { + continue; + } + + + if (($directory_depth < 1 OR $new_depth > 0) && is_dir($source_dir . $file)) { + $aTmp = $this->_assetsMap($source_dir . $file . '/', $new_depth, $hidden); + if(!empty($aTmp)) { + asort($aTmp, SORT_STRING | SORT_FLAG_CASE | SORT_NATURAL); + $filedata[$file] = $aTmp; + } + unset($aTmp); + } else { + $sFileExt = strtolower(pathinfo($file, PATHINFO_EXTENSION)); + if(!empty($this->_mFileExt) && in_array($sFileExt, $this->_mFileExt)) { + $filedata[] = $file; + } + } + } + + closedir($fp); + return $filedata; + } + echo 'can not open dir'; + return FALSE; + } + } -$tpl->generate($cfg['path']['templates'] . $cfg['templates']['files_overview']); +$oDirList = new clFileList($cfgClient[$client]["js"]["path"], 'js'); +$oDirList->scanDir(); -?> \ No newline at end of file +$oDirList->renderList(); \ No newline at end of file diff --git a/conlite/includes/include.style_edit_form.php b/conlite/includes/include.style_edit_form.php index 68047d6..078bcc7 100644 --- a/conlite/includes/include.style_edit_form.php +++ b/conlite/includes/include.style_edit_form.php @@ -158,7 +158,7 @@ if (!$perm->have_perm_area_action($area, $action)) { if ($_REQUEST['action'] == $sActionEdit) { $sCode = getFileContent($sFilename, $path); } else { - $sCode = stripslashes($_REQUEST['code']); # stripslashes is required here in case of creating a new file + $sCode = $_REQUEST['code']; # stripslashes is required here in case of creating a new file } $aFileInfo = getFileInformation($client, $sTempFilename, "css", $db); diff --git a/conlite/scripts/fancytree/LICENSE.txt b/conlite/scripts/fancytree/LICENSE.txt new file mode 100644 index 0000000..ffeb70b --- /dev/null +++ b/conlite/scripts/fancytree/LICENSE.txt @@ -0,0 +1,21 @@ +Copyright 2008-2021 Martin Wendt, +https://wwWendt.de/ + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/conlite/scripts/fancytree/jquery.fancytree-all.js b/conlite/scripts/fancytree/jquery.fancytree-all.js new file mode 100644 index 0000000..c952854 --- /dev/null +++ b/conlite/scripts/fancytree/jquery.fancytree-all.js @@ -0,0 +1,13154 @@ +/*! + * jquery.fancytree.js + * Tree view control with support for lazy loading and much more. + * https://github.com/mar10/fancytree/ + * + * Copyright (c) 2008-2021, Martin Wendt (https://wwWendt.de) + * Released under the MIT license + * https://github.com/mar10/fancytree/wiki/LicenseInfo + * + * @version 2.38.0 + * @date 2021-02-09T20:03:49Z + */ + +/** Core Fancytree module. + */ + +// UMD wrapper for the Fancytree core module +(function(factory) { + if (typeof define === "function" && define.amd) { + // AMD. Register as an anonymous module. + define(["jquery", "./jquery.fancytree.ui-deps"], factory); + } else if (typeof module === "object" && module.exports) { + // Node/CommonJS + require("./jquery.fancytree.ui-deps"); + module.exports = factory(require("jquery")); + } else { + // Browser globals + factory(jQuery); + } +})(function($) { + "use strict"; + + // prevent duplicate loading + if ($.ui && $.ui.fancytree) { + $.ui.fancytree.warn("Fancytree: ignored duplicate include"); + return; + } + + /****************************************************************************** + * Private functions and variables + */ + + var i, + attr, + FT = null, // initialized below + TEST_IMG = new RegExp(/\.|\//), // strings are considered image urls if they contain '.' or '/' + REX_HTML = /[&<>"'/]/g, // Escape those characters + REX_TOOLTIP = /[<>"'/]/g, // Don't escape `&` in tooltips + RECURSIVE_REQUEST_ERROR = "$recursive_request", + INVALID_REQUEST_TARGET_ERROR = "$request_target_invalid", + ENTITY_MAP = { + "&": "&", + "<": "<", + ">": ">", + '"': """, + "'": "'", + "/": "/", + }, + IGNORE_KEYCODES = { 16: true, 17: true, 18: true }, + SPECIAL_KEYCODES = { + 8: "backspace", + 9: "tab", + 10: "return", + 13: "return", + // 16: null, 17: null, 18: null, // ignore shift, ctrl, alt + 19: "pause", + 20: "capslock", + 27: "esc", + 32: "space", + 33: "pageup", + 34: "pagedown", + 35: "end", + 36: "home", + 37: "left", + 38: "up", + 39: "right", + 40: "down", + 45: "insert", + 46: "del", + 59: ";", + 61: "=", + // 91: null, 93: null, // ignore left and right meta + 96: "0", + 97: "1", + 98: "2", + 99: "3", + 100: "4", + 101: "5", + 102: "6", + 103: "7", + 104: "8", + 105: "9", + 106: "*", + 107: "+", + 109: "-", + 110: ".", + 111: "/", + 112: "f1", + 113: "f2", + 114: "f3", + 115: "f4", + 116: "f5", + 117: "f6", + 118: "f7", + 119: "f8", + 120: "f9", + 121: "f10", + 122: "f11", + 123: "f12", + 144: "numlock", + 145: "scroll", + 173: "-", + 186: ";", + 187: "=", + 188: ",", + 189: "-", + 190: ".", + 191: "/", + 192: "`", + 219: "[", + 220: "\\", + 221: "]", + 222: "'", + }, + MODIFIERS = { + 16: "shift", + 17: "ctrl", + 18: "alt", + 91: "meta", + 93: "meta", + }, + MOUSE_BUTTONS = { 0: "", 1: "left", 2: "middle", 3: "right" }, + // Boolean attributes that can be set with equivalent class names in the LI tags + // Note: v2.23: checkbox and hideCheckbox are *not* in this list + CLASS_ATTRS = "active expanded focus folder lazy radiogroup selected unselectable unselectableIgnore".split( + " " + ), + CLASS_ATTR_MAP = {}, + // Top-level Fancytree attributes, that can be set by dict + TREE_ATTRS = "columns types".split(" "), + // TREE_ATTR_MAP = {}, + // Top-level FancytreeNode attributes, that can be set by dict + NODE_ATTRS = "checkbox expanded extraClasses folder icon iconTooltip key lazy partsel radiogroup refKey selected statusNodeType title tooltip type unselectable unselectableIgnore unselectableStatus".split( + " " + ), + NODE_ATTR_MAP = {}, + // Mapping of lowercase -> real name (because HTML5 data-... attribute only supports lowercase) + NODE_ATTR_LOWERCASE_MAP = {}, + // Attribute names that should NOT be added to node.data + NONE_NODE_DATA_MAP = { + active: true, + children: true, + data: true, + focus: true, + }; + + for (i = 0; i < CLASS_ATTRS.length; i++) { + CLASS_ATTR_MAP[CLASS_ATTRS[i]] = true; + } + for (i = 0; i < NODE_ATTRS.length; i++) { + attr = NODE_ATTRS[i]; + NODE_ATTR_MAP[attr] = true; + if (attr !== attr.toLowerCase()) { + NODE_ATTR_LOWERCASE_MAP[attr.toLowerCase()] = attr; + } + } + // for(i=0; i t; + } + } + return true; + } + + /** + * Deep-merge a list of objects (but replace array-type options). + * + * jQuery's $.extend(true, ...) method does a deep merge, that also merges Arrays. + * This variant is used to merge extension defaults with user options, and should + * merge objects, but override arrays (for example the `triggerStart: [...]` option + * of ext-edit). Also `null` values are copied over and not skipped. + * + * See issue #876 + * + * Example: + * _simpleDeepMerge({}, o1, o2); + */ + function _simpleDeepMerge() { + var options, + name, + src, + copy, + clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length; + + // Handle case when target is a string or something (possible in deep copy) + if (typeof target !== "object" && !$.isFunction(target)) { + target = {}; + } + if (i === length) { + throw Error("need at least two args"); + } + for (; i < length; i++) { + // Only deal with non-null/undefined values + if ((options = arguments[i]) != null) { + // Extend the base object + for (name in options) { + if (options.hasOwnProperty(name)) { + src = target[name]; + copy = options[name]; + // Prevent never-ending loop + if (target === copy) { + continue; + } + // Recurse if we're merging plain objects + // (NOTE: unlike $.extend, we don't merge arrays, but replace them) + if (copy && $.isPlainObject(copy)) { + clone = src && $.isPlainObject(src) ? src : {}; + // Never move original objects, clone them + target[name] = _simpleDeepMerge(clone, copy); + // Don't bring in undefined values + } else if (copy !== undefined) { + target[name] = copy; + } + } + } + } + } + // Return the modified object + return target; + } + + /** Return a wrapper that calls sub.methodName() and exposes + * this : tree + * this._local : tree.ext.EXTNAME + * this._super : base.methodName.call() + * this._superApply : base.methodName.apply() + */ + function _makeVirtualFunction(methodName, tree, base, extension, extName) { + // $.ui.fancytree.debug("_makeVirtualFunction", methodName, tree, base, extension, extName); + // if(rexTestSuper && !rexTestSuper.test(func)){ + // // extension.methodName() doesn't call _super(), so no wrapper required + // return func; + // } + // Use an immediate function as closure + var proxy = (function() { + var prevFunc = tree[methodName], // org. tree method or prev. proxy + baseFunc = extension[methodName], // + _local = tree.ext[extName], + _super = function() { + return prevFunc.apply(tree, arguments); + }, + _superApply = function(args) { + return prevFunc.apply(tree, args); + }; + + // Return the wrapper function + return function() { + var prevLocal = tree._local, + prevSuper = tree._super, + prevSuperApply = tree._superApply; + + try { + tree._local = _local; + tree._super = _super; + tree._superApply = _superApply; + return baseFunc.apply(tree, arguments); + } finally { + tree._local = prevLocal; + tree._super = prevSuper; + tree._superApply = prevSuperApply; + } + }; + })(); // end of Immediate Function + return proxy; + } + + /** + * Subclass `base` by creating proxy functions + */ + function _subclassObject(tree, base, extension, extName) { + // $.ui.fancytree.debug("_subclassObject", tree, base, extension, extName); + for (var attrName in extension) { + if (typeof extension[attrName] === "function") { + if (typeof tree[attrName] === "function") { + // override existing method + tree[attrName] = _makeVirtualFunction( + attrName, + tree, + base, + extension, + extName + ); + } else if (attrName.charAt(0) === "_") { + // Create private methods in tree.ext.EXTENSION namespace + tree.ext[extName][attrName] = _makeVirtualFunction( + attrName, + tree, + base, + extension, + extName + ); + } else { + $.error( + "Could not override tree." + + attrName + + ". Use prefix '_' to create tree." + + extName + + "._" + + attrName + ); + } + } else { + // Create member variables in tree.ext.EXTENSION namespace + if (attrName !== "options") { + tree.ext[extName][attrName] = extension[attrName]; + } + } + } + } + + function _getResolvedPromise(context, argArray) { + if (context === undefined) { + return $.Deferred(function() { + this.resolve(); + }).promise(); + } + return $.Deferred(function() { + this.resolveWith(context, argArray); + }).promise(); + } + + function _getRejectedPromise(context, argArray) { + if (context === undefined) { + return $.Deferred(function() { + this.reject(); + }).promise(); + } + return $.Deferred(function() { + this.rejectWith(context, argArray); + }).promise(); + } + + function _makeResolveFunc(deferred, context) { + return function() { + deferred.resolveWith(context); + }; + } + + function _getElementDataAsDict($el) { + // Evaluate 'data-NAME' attributes with special treatment for 'data-json'. + var d = $.extend({}, $el.data()), + json = d.json; + + delete d.fancytree; // added to container by widget factory (old jQuery UI) + delete d.uiFancytree; // added to container by widget factory + + if (json) { + delete d.json; + //
  • is already returned as object (http://api.jquery.com/data/#data-html5) + d = $.extend(d, json); + } + return d; + } + + function _escapeTooltip(s) { + return ("" + s).replace(REX_TOOLTIP, function(s) { + return ENTITY_MAP[s]; + }); + } + + // TODO: use currying + function _makeNodeTitleMatcher(s) { + s = s.toLowerCase(); + return function(node) { + return node.title.toLowerCase().indexOf(s) >= 0; + }; + } + + function _makeNodeTitleStartMatcher(s) { + var reMatch = new RegExp("^" + s, "i"); + return function(node) { + return reMatch.test(node.title); + }; + } + + /****************************************************************************** + * FancytreeNode + */ + + /** + * Creates a new FancytreeNode + * + * @class FancytreeNode + * @classdesc A FancytreeNode represents the hierarchical data model and operations. + * + * @param {FancytreeNode} parent + * @param {NodeData} obj + * + * @property {Fancytree} tree The tree instance + * @property {FancytreeNode} parent The parent node + * @property {string} key Node id (must be unique inside the tree) + * @property {string} title Display name (may contain HTML) + * @property {object} data Contains all extra data that was passed on node creation + * @property {FancytreeNode[] | null | undefined} children Array of child nodes.
    + * For lazy nodes, null or undefined means 'not yet loaded'. Use an empty array + * to define a node that has no children. + * @property {boolean} expanded Use isExpanded(), setExpanded() to access this property. + * @property {string} extraClasses Additional CSS classes, added to the node's ``.
    + * Note: use `node.add/remove/toggleClass()` to modify. + * @property {boolean} folder Folder nodes have different default icons and click behavior.
    + * Note: Also non-folders may have children. + * @property {string} statusNodeType null for standard nodes. Otherwise type of special system node: 'error', 'loading', 'nodata', or 'paging'. + * @property {boolean} lazy True if this node is loaded on demand, i.e. on first expansion. + * @property {boolean} selected Use isSelected(), setSelected() to access this property. + * @property {string} tooltip Alternative description used as hover popup + * @property {string} iconTooltip Description used as hover popup for icon. @since 2.27 + * @property {string} type Node type, used with tree.types map. @since 2.27 + */ + function FancytreeNode(parent, obj) { + var i, l, name, cl; + + this.parent = parent; + this.tree = parent.tree; + this.ul = null; + this.li = null; //
  • tag + this.statusNodeType = null; // if this is a temp. node to display the status of its parent + this._isLoading = false; // if this node itself is loading + this._error = null; // {message: '...'} if a load error occurred + this.data = {}; + + // TODO: merge this code with node.toDict() + // copy attributes from obj object + for (i = 0, l = NODE_ATTRS.length; i < l; i++) { + name = NODE_ATTRS[i]; + this[name] = obj[name]; + } + // unselectableIgnore and unselectableStatus imply unselectable + if ( + this.unselectableIgnore != null || + this.unselectableStatus != null + ) { + this.unselectable = true; + } + if (obj.hideCheckbox) { + $.error( + "'hideCheckbox' node option was removed in v2.23.0: use 'checkbox: false'" + ); + } + // node.data += obj.data + if (obj.data) { + $.extend(this.data, obj.data); + } + // Copy all other attributes to this.data.NAME + for (name in obj) { + if ( + !NODE_ATTR_MAP[name] && + (this.tree.options.copyFunctionsToData || + !$.isFunction(obj[name])) && + !NONE_NODE_DATA_MAP[name] + ) { + // node.data.NAME = obj.NAME + this.data[name] = obj[name]; + } + } + + // Fix missing key + if (this.key == null) { + // test for null OR undefined + if (this.tree.options.defaultKey) { + this.key = "" + this.tree.options.defaultKey(this); + _assert(this.key, "defaultKey() must return a unique key"); + } else { + this.key = "_" + FT._nextNodeKey++; + } + } else { + this.key = "" + this.key; // Convert to string (#217) + } + + // Fix tree.activeNode + // TODO: not elegant: we use obj.active as marker to set tree.activeNode + // when loading from a dictionary. + if (obj.active) { + _assert( + this.tree.activeNode === null, + "only one active node allowed" + ); + this.tree.activeNode = this; + } + if (obj.selected) { + // #186 + this.tree.lastSelectedNode = this; + } + // TODO: handle obj.focus = true + + // Create child nodes + cl = obj.children; + if (cl) { + if (cl.length) { + this._setChildren(cl); + } else { + // if an empty array was passed for a lazy node, keep it, in order to mark it 'loaded' + this.children = this.lazy ? [] : null; + } + } else { + this.children = null; + } + // Add to key/ref map (except for root node) + // if( parent ) { + this.tree._callHook("treeRegisterNode", this.tree, true, this); + // } + } + + FancytreeNode.prototype = /** @lends FancytreeNode# */ { + /* Return the direct child FancytreeNode with a given key, index. */ + _findDirectChild: function(ptr) { + var i, + l, + cl = this.children; + + if (cl) { + if (typeof ptr === "string") { + for (i = 0, l = cl.length; i < l; i++) { + if (cl[i].key === ptr) { + return cl[i]; + } + } + } else if (typeof ptr === "number") { + return this.children[ptr]; + } else if (ptr.parent === this) { + return ptr; + } + } + return null; + }, + // TODO: activate() + // TODO: activateSilently() + /* Internal helper called in recursive addChildren sequence.*/ + _setChildren: function(children) { + _assert( + children && (!this.children || this.children.length === 0), + "only init supported" + ); + this.children = []; + for (var i = 0, l = children.length; i < l; i++) { + this.children.push(new FancytreeNode(this, children[i])); + } + this.tree._callHook( + "treeStructureChanged", + this.tree, + "setChildren" + ); + }, + /** + * Append (or insert) a list of child nodes. + * + * @param {NodeData[]} children array of child node definitions (also single child accepted) + * @param {FancytreeNode | string | Integer} [insertBefore] child node (or key or index of such). + * If omitted, the new children are appended. + * @returns {FancytreeNode} first child added + * + * @see FancytreeNode#applyPatch + */ + addChildren: function(children, insertBefore) { + var i, + l, + pos, + origFirstChild = this.getFirstChild(), + origLastChild = this.getLastChild(), + firstNode = null, + nodeList = []; + + if ($.isPlainObject(children)) { + children = [children]; + } + if (!this.children) { + this.children = []; + } + for (i = 0, l = children.length; i < l; i++) { + nodeList.push(new FancytreeNode(this, children[i])); + } + firstNode = nodeList[0]; + if (insertBefore == null) { + this.children = this.children.concat(nodeList); + } else { + // Returns null if insertBefore is not a direct child: + insertBefore = this._findDirectChild(insertBefore); + pos = $.inArray(insertBefore, this.children); + _assert(pos >= 0, "insertBefore must be an existing child"); + // insert nodeList after children[pos] + this.children.splice.apply( + this.children, + [pos, 0].concat(nodeList) + ); + } + if (origFirstChild && !insertBefore) { + // #708: Fast path -- don't render every child of root, just the new ones! + // #723, #729: but only if it's appended to an existing child list + for (i = 0, l = nodeList.length; i < l; i++) { + nodeList[i].render(); // New nodes were never rendered before + } + // Adjust classes where status may have changed + // Has a first child + if (origFirstChild !== this.getFirstChild()) { + // Different first child -- recompute classes + origFirstChild.renderStatus(); + } + if (origLastChild !== this.getLastChild()) { + // Different last child -- recompute classes + origLastChild.renderStatus(); + } + } else if (!this.parent || this.parent.ul || this.tr) { + // render if the parent was rendered (or this is a root node) + this.render(); + } + if (this.tree.options.selectMode === 3) { + this.fixSelection3FromEndNodes(); + } + this.triggerModifyChild( + "add", + nodeList.length === 1 ? nodeList[0] : null + ); + return firstNode; + }, + /** + * Add class to node's span tag and to .extraClasses. + * + * @param {string} className class name + * + * @since 2.17 + */ + addClass: function(className) { + return this.toggleClass(className, true); + }, + /** + * Append or prepend a node, or append a child node. + * + * This a convenience function that calls addChildren() + * + * @param {NodeData} node node definition + * @param {string} [mode=child] 'before', 'after', 'firstChild', or 'child' ('over' is a synonym for 'child') + * @returns {FancytreeNode} new node + */ + addNode: function(node, mode) { + if (mode === undefined || mode === "over") { + mode = "child"; + } + switch (mode) { + case "after": + return this.getParent().addChildren( + node, + this.getNextSibling() + ); + case "before": + return this.getParent().addChildren(node, this); + case "firstChild": + // Insert before the first child if any + var insertBefore = this.children ? this.children[0] : null; + return this.addChildren(node, insertBefore); + case "child": + case "over": + return this.addChildren(node); + } + _assert(false, "Invalid mode: " + mode); + }, + /**Add child status nodes that indicate 'More...', etc. + * + * This also maintains the node's `partload` property. + * @param {boolean|object} node optional node definition. Pass `false` to remove all paging nodes. + * @param {string} [mode='child'] 'child'|firstChild' + * @since 2.15 + */ + addPagingNode: function(node, mode) { + var i, n; + + mode = mode || "child"; + if (node === false) { + for (i = this.children.length - 1; i >= 0; i--) { + n = this.children[i]; + if (n.statusNodeType === "paging") { + this.removeChild(n); + } + } + this.partload = false; + return; + } + node = $.extend( + { + title: this.tree.options.strings.moreData, + statusNodeType: "paging", + icon: false, + }, + node + ); + this.partload = true; + return this.addNode(node, mode); + }, + /** + * Append new node after this. + * + * This a convenience function that calls addNode(node, 'after') + * + * @param {NodeData} node node definition + * @returns {FancytreeNode} new node + */ + appendSibling: function(node) { + return this.addNode(node, "after"); + }, + /** + * (experimental) Apply a modification (or navigation) operation. + * + * @param {string} cmd + * @param {object} [opts] + * @see Fancytree#applyCommand + * @since 2.32 + */ + applyCommand: function(cmd, opts) { + return this.tree.applyCommand(cmd, this, opts); + }, + /** + * Modify existing child nodes. + * + * @param {NodePatch} patch + * @returns {$.Promise} + * @see FancytreeNode#addChildren + */ + applyPatch: function(patch) { + // patch [key, null] means 'remove' + if (patch === null) { + this.remove(); + return _getResolvedPromise(this); + } + // TODO: make sure that root node is not collapsed or modified + // copy (most) attributes to node.ATTR or node.data.ATTR + var name, + promise, + v, + IGNORE_MAP = { children: true, expanded: true, parent: true }; // TODO: should be global + + for (name in patch) { + if (patch.hasOwnProperty(name)) { + v = patch[name]; + if (!IGNORE_MAP[name] && !$.isFunction(v)) { + if (NODE_ATTR_MAP[name]) { + this[name] = v; + } else { + this.data[name] = v; + } + } + } + } + // Remove and/or create children + if (patch.hasOwnProperty("children")) { + this.removeChildren(); + if (patch.children) { + // only if not null and not empty list + // TODO: addChildren instead? + this._setChildren(patch.children); + } + // TODO: how can we APPEND or INSERT child nodes? + } + if (this.isVisible()) { + this.renderTitle(); + this.renderStatus(); + } + // Expand collapse (final step, since this may be async) + if (patch.hasOwnProperty("expanded")) { + promise = this.setExpanded(patch.expanded); + } else { + promise = _getResolvedPromise(this); + } + return promise; + }, + /** Collapse all sibling nodes. + * @returns {$.Promise} + */ + collapseSiblings: function() { + return this.tree._callHook("nodeCollapseSiblings", this); + }, + /** Copy this node as sibling or child of `node`. + * + * @param {FancytreeNode} node source node + * @param {string} [mode=child] 'before' | 'after' | 'child' + * @param {Function} [map] callback function(NodeData, FancytreeNode) that could modify the new node + * @returns {FancytreeNode} new + */ + copyTo: function(node, mode, map) { + return node.addNode(this.toDict(true, map), mode); + }, + /** Count direct and indirect children. + * + * @param {boolean} [deep=true] pass 'false' to only count direct children + * @returns {int} number of child nodes + */ + countChildren: function(deep) { + var cl = this.children, + i, + l, + n; + if (!cl) { + return 0; + } + n = cl.length; + if (deep !== false) { + for (i = 0, l = n; i < l; i++) { + n += cl[i].countChildren(); + } + } + return n; + }, + // TODO: deactivate() + /** Write to browser console if debugLevel >= 4 (prepending node info) + * + * @param {*} msg string or object or array of such + */ + debug: function(msg) { + if (this.tree.options.debugLevel >= 4) { + Array.prototype.unshift.call(arguments, this.toString()); + consoleApply("log", arguments); + } + }, + /** Deprecated. + * @deprecated since 2014-02-16. Use resetLazy() instead. + */ + discard: function() { + this.warn( + "FancytreeNode.discard() is deprecated since 2014-02-16. Use .resetLazy() instead." + ); + return this.resetLazy(); + }, + /** Remove DOM elements for all descendents. May be called on .collapse event + * to keep the DOM small. + * @param {boolean} [includeSelf=false] + */ + discardMarkup: function(includeSelf) { + var fn = includeSelf ? "nodeRemoveMarkup" : "nodeRemoveChildMarkup"; + this.tree._callHook(fn, this); + }, + /** Write error to browser console if debugLevel >= 1 (prepending tree info) + * + * @param {*} msg string or object or array of such + */ + error: function(msg) { + if (this.tree.options.debugLevel >= 1) { + Array.prototype.unshift.call(arguments, this.toString()); + consoleApply("error", arguments); + } + }, + /**Find all nodes that match condition (excluding self). + * + * @param {string | function(node)} match title string to search for, or a + * callback function that returns `true` if a node is matched. + * @returns {FancytreeNode[]} array of nodes (may be empty) + */ + findAll: function(match) { + match = $.isFunction(match) ? match : _makeNodeTitleMatcher(match); + var res = []; + this.visit(function(n) { + if (match(n)) { + res.push(n); + } + }); + return res; + }, + /**Find first node that matches condition (excluding self). + * + * @param {string | function(node)} match title string to search for, or a + * callback function that returns `true` if a node is matched. + * @returns {FancytreeNode} matching node or null + * @see FancytreeNode#findAll + */ + findFirst: function(match) { + match = $.isFunction(match) ? match : _makeNodeTitleMatcher(match); + var res = null; + this.visit(function(n) { + if (match(n)) { + res = n; + return false; + } + }); + return res; + }, + /** Find a node relative to self. + * + * @param {number|string} where The keyCode that would normally trigger this move, + * or a keyword ('down', 'first', 'last', 'left', 'parent', 'right', 'up'). + * @returns {FancytreeNode} + * @since v2.31 + */ + findRelatedNode: function(where, includeHidden) { + return this.tree.findRelatedNode(this, where, includeHidden); + }, + /* Apply selection state (internal use only) */ + _changeSelectStatusAttrs: function(state) { + var changed = false, + opts = this.tree.options, + unselectable = FT.evalOption( + "unselectable", + this, + this, + opts, + false + ), + unselectableStatus = FT.evalOption( + "unselectableStatus", + this, + this, + opts, + undefined + ); + + if (unselectable && unselectableStatus != null) { + state = unselectableStatus; + } + switch (state) { + case false: + changed = this.selected || this.partsel; + this.selected = false; + this.partsel = false; + break; + case true: + changed = !this.selected || !this.partsel; + this.selected = true; + this.partsel = true; + break; + case undefined: + changed = this.selected || !this.partsel; + this.selected = false; + this.partsel = true; + break; + default: + _assert(false, "invalid state: " + state); + } + // this.debug("fixSelection3AfterLoad() _changeSelectStatusAttrs()", state, changed); + if (changed) { + this.renderStatus(); + } + return changed; + }, + /** + * Fix selection status, after this node was (de)selected in multi-hier mode. + * This includes (de)selecting all children. + */ + fixSelection3AfterClick: function(callOpts) { + var flag = this.isSelected(); + + // this.debug("fixSelection3AfterClick()"); + + this.visit(function(node) { + node._changeSelectStatusAttrs(flag); + if (node.radiogroup) { + // #931: don't (de)select this branch + return "skip"; + } + }); + this.fixSelection3FromEndNodes(callOpts); + }, + /** + * Fix selection status for multi-hier mode. + * Only end-nodes are considered to update the descendants branch and parents. + * Should be called after this node has loaded new children or after + * children have been modified using the API. + */ + fixSelection3FromEndNodes: function(callOpts) { + var opts = this.tree.options; + + // this.debug("fixSelection3FromEndNodes()"); + _assert(opts.selectMode === 3, "expected selectMode 3"); + + // Visit all end nodes and adjust their parent's `selected` and `partsel` + // attributes. Return selection state true, false, or undefined. + function _walk(node) { + var i, + l, + child, + s, + state, + allSelected, + someSelected, + unselIgnore, + unselState, + children = node.children; + + if (children && children.length) { + // check all children recursively + allSelected = true; + someSelected = false; + + for (i = 0, l = children.length; i < l; i++) { + child = children[i]; + // the selection state of a node is not relevant; we need the end-nodes + s = _walk(child); + // if( !child.unselectableIgnore ) { + unselIgnore = FT.evalOption( + "unselectableIgnore", + child, + child, + opts, + false + ); + if (!unselIgnore) { + if (s !== false) { + someSelected = true; + } + if (s !== true) { + allSelected = false; + } + } + } + // eslint-disable-next-line no-nested-ternary + state = allSelected + ? true + : someSelected + ? undefined + : false; + } else { + // This is an end-node: simply report the status + unselState = FT.evalOption( + "unselectableStatus", + node, + node, + opts, + undefined + ); + state = unselState == null ? !!node.selected : !!unselState; + } + // #939: Keep a `partsel` flag that was explicitly set on a lazy node + if ( + node.partsel && + !node.selected && + node.lazy && + node.children == null + ) { + state = undefined; + } + node._changeSelectStatusAttrs(state); + return state; + } + _walk(this); + + // Update parent's state + this.visitParents(function(node) { + var i, + l, + child, + state, + unselIgnore, + unselState, + children = node.children, + allSelected = true, + someSelected = false; + + for (i = 0, l = children.length; i < l; i++) { + child = children[i]; + unselIgnore = FT.evalOption( + "unselectableIgnore", + child, + child, + opts, + false + ); + if (!unselIgnore) { + unselState = FT.evalOption( + "unselectableStatus", + child, + child, + opts, + undefined + ); + state = + unselState == null + ? !!child.selected + : !!unselState; + // When fixing the parents, we trust the sibling status (i.e. + // we don't recurse) + if (state || child.partsel) { + someSelected = true; + } + if (!state) { + allSelected = false; + } + } + } + // eslint-disable-next-line no-nested-ternary + state = allSelected ? true : someSelected ? undefined : false; + node._changeSelectStatusAttrs(state); + }); + }, + // TODO: focus() + /** + * Update node data. If dict contains 'children', then also replace + * the hole sub tree. + * @param {NodeData} dict + * + * @see FancytreeNode#addChildren + * @see FancytreeNode#applyPatch + */ + fromDict: function(dict) { + // copy all other attributes to this.data.xxx + for (var name in dict) { + if (NODE_ATTR_MAP[name]) { + // node.NAME = dict.NAME + this[name] = dict[name]; + } else if (name === "data") { + // node.data += dict.data + $.extend(this.data, dict.data); + } else if ( + !$.isFunction(dict[name]) && + !NONE_NODE_DATA_MAP[name] + ) { + // node.data.NAME = dict.NAME + this.data[name] = dict[name]; + } + } + if (dict.children) { + // recursively set children and render + this.removeChildren(); + this.addChildren(dict.children); + } + this.renderTitle(); + /* + var children = dict.children; + if(children === undefined){ + this.data = $.extend(this.data, dict); + this.render(); + return; + } + dict = $.extend({}, dict); + dict.children = undefined; + this.data = $.extend(this.data, dict); + this.removeChildren(); + this.addChild(children); + */ + }, + /** Return the list of child nodes (undefined for unexpanded lazy nodes). + * @returns {FancytreeNode[] | undefined} + */ + getChildren: function() { + if (this.hasChildren() === undefined) { + // TODO: only required for lazy nodes? + return undefined; // Lazy node: unloaded, currently loading, or load error + } + return this.children; + }, + /** Return the first child node or null. + * @returns {FancytreeNode | null} + */ + getFirstChild: function() { + return this.children ? this.children[0] : null; + }, + /** Return the 0-based child index. + * @returns {int} + */ + getIndex: function() { + // return this.parent.children.indexOf(this); + return $.inArray(this, this.parent.children); // indexOf doesn't work in IE7 + }, + /** Return the hierarchical child index (1-based, e.g. '3.2.4'). + * @param {string} [separator="."] + * @param {int} [digits=1] + * @returns {string} + */ + getIndexHier: function(separator, digits) { + separator = separator || "."; + var s, + res = []; + $.each(this.getParentList(false, true), function(i, o) { + s = "" + (o.getIndex() + 1); + if (digits) { + // prepend leading zeroes + s = ("0000000" + s).substr(-digits); + } + res.push(s); + }); + return res.join(separator); + }, + /** Return the parent keys separated by options.keyPathSeparator, e.g. "/id_1/id_17/id_32". + * + * (Unlike `node.getPath()`, this method prepends a "/" and inverts the first argument.) + * + * @see FancytreeNode#getPath + * @param {boolean} [excludeSelf=false] + * @returns {string} + */ + getKeyPath: function(excludeSelf) { + var sep = this.tree.options.keyPathSeparator; + + return sep + this.getPath(!excludeSelf, "key", sep); + }, + /** Return the last child of this node or null. + * @returns {FancytreeNode | null} + */ + getLastChild: function() { + return this.children + ? this.children[this.children.length - 1] + : null; + }, + /** Return node depth. 0: System root node, 1: visible top-level node, 2: first sub-level, ... . + * @returns {int} + */ + getLevel: function() { + var level = 0, + dtn = this.parent; + while (dtn) { + level++; + dtn = dtn.parent; + } + return level; + }, + /** Return the successor node (under the same parent) or null. + * @returns {FancytreeNode | null} + */ + getNextSibling: function() { + // TODO: use indexOf, if available: (not in IE6) + if (this.parent) { + var i, + l, + ac = this.parent.children; + + for (i = 0, l = ac.length - 1; i < l; i++) { + // up to length-2, so next(last) = null + if (ac[i] === this) { + return ac[i + 1]; + } + } + } + return null; + }, + /** Return the parent node (null for the system root node). + * @returns {FancytreeNode | null} + */ + getParent: function() { + // TODO: return null for top-level nodes? + return this.parent; + }, + /** Return an array of all parent nodes (top-down). + * @param {boolean} [includeRoot=false] Include the invisible system root node. + * @param {boolean} [includeSelf=false] Include the node itself. + * @returns {FancytreeNode[]} + */ + getParentList: function(includeRoot, includeSelf) { + var l = [], + dtn = includeSelf ? this : this.parent; + while (dtn) { + if (includeRoot || dtn.parent) { + l.unshift(dtn); + } + dtn = dtn.parent; + } + return l; + }, + /** Return a string representing the hierachical node path, e.g. "a/b/c". + * @param {boolean} [includeSelf=true] + * @param {string | function} [part="title"] node property name or callback + * @param {string} [separator="/"] + * @returns {string} + * @since v2.31 + */ + getPath: function(includeSelf, part, separator) { + includeSelf = includeSelf !== false; + part = part || "title"; + separator = separator || "/"; + + var val, + path = [], + isFunc = $.isFunction(part); + + this.visitParents(function(n) { + if (n.parent) { + val = isFunc ? part(n) : n[part]; + path.unshift(val); + } + }, includeSelf); + return path.join(separator); + }, + /** Return the predecessor node (under the same parent) or null. + * @returns {FancytreeNode | null} + */ + getPrevSibling: function() { + if (this.parent) { + var i, + l, + ac = this.parent.children; + + for (i = 1, l = ac.length; i < l; i++) { + // start with 1, so prev(first) = null + if (ac[i] === this) { + return ac[i - 1]; + } + } + } + return null; + }, + /** + * Return an array of selected descendant nodes. + * @param {boolean} [stopOnParents=false] only return the topmost selected + * node (useful with selectMode 3) + * @returns {FancytreeNode[]} + */ + getSelectedNodes: function(stopOnParents) { + var nodeList = []; + this.visit(function(node) { + if (node.selected) { + nodeList.push(node); + if (stopOnParents === true) { + return "skip"; // stop processing this branch + } + } + }); + return nodeList; + }, + /** Return true if node has children. Return undefined if not sure, i.e. the node is lazy and not yet loaded). + * @returns {boolean | undefined} + */ + hasChildren: function() { + if (this.lazy) { + if (this.children == null) { + // null or undefined: Not yet loaded + return undefined; + } else if (this.children.length === 0) { + // Loaded, but response was empty + return false; + } else if ( + this.children.length === 1 && + this.children[0].isStatusNode() + ) { + // Currently loading or load error + return undefined; + } + return true; + } + return !!(this.children && this.children.length); + }, + /** + * Return true if node has `className` defined in .extraClasses. + * + * @param {string} className class name (separate multiple classes by space) + * @returns {boolean} + * + * @since 2.32 + */ + hasClass: function(className) { + return ( + (" " + (this.extraClasses || "") + " ").indexOf( + " " + className + " " + ) >= 0 + ); + }, + /** Return true if node has keyboard focus. + * @returns {boolean} + */ + hasFocus: function() { + return this.tree.hasFocus() && this.tree.focusNode === this; + }, + /** Write to browser console if debugLevel >= 3 (prepending node info) + * + * @param {*} msg string or object or array of such + */ + info: function(msg) { + if (this.tree.options.debugLevel >= 3) { + Array.prototype.unshift.call(arguments, this.toString()); + consoleApply("info", arguments); + } + }, + /** Return true if node is active (see also FancytreeNode#isSelected). + * @returns {boolean} + */ + isActive: function() { + return this.tree.activeNode === this; + }, + /** Return true if node is vertically below `otherNode`, i.e. rendered in a subsequent row. + * @param {FancytreeNode} otherNode + * @returns {boolean} + * @since 2.28 + */ + isBelowOf: function(otherNode) { + return this.getIndexHier(".", 5) > otherNode.getIndexHier(".", 5); + }, + /** Return true if node is a direct child of otherNode. + * @param {FancytreeNode} otherNode + * @returns {boolean} + */ + isChildOf: function(otherNode) { + return this.parent && this.parent === otherNode; + }, + /** Return true, if node is a direct or indirect sub node of otherNode. + * @param {FancytreeNode} otherNode + * @returns {boolean} + */ + isDescendantOf: function(otherNode) { + if (!otherNode || otherNode.tree !== this.tree) { + return false; + } + var p = this.parent; + while (p) { + if (p === otherNode) { + return true; + } + if (p === p.parent) { + $.error("Recursive parent link: " + p); + } + p = p.parent; + } + return false; + }, + /** Return true if node is expanded. + * @returns {boolean} + */ + isExpanded: function() { + return !!this.expanded; + }, + /** Return true if node is the first node of its parent's children. + * @returns {boolean} + */ + isFirstSibling: function() { + var p = this.parent; + return !p || p.children[0] === this; + }, + /** Return true if node is a folder, i.e. has the node.folder attribute set. + * @returns {boolean} + */ + isFolder: function() { + return !!this.folder; + }, + /** Return true if node is the last node of its parent's children. + * @returns {boolean} + */ + isLastSibling: function() { + var p = this.parent; + return !p || p.children[p.children.length - 1] === this; + }, + /** Return true if node is lazy (even if data was already loaded) + * @returns {boolean} + */ + isLazy: function() { + return !!this.lazy; + }, + /** Return true if node is lazy and loaded. For non-lazy nodes always return true. + * @returns {boolean} + */ + isLoaded: function() { + return !this.lazy || this.hasChildren() !== undefined; // Also checks if the only child is a status node + }, + /** Return true if children are currently beeing loaded, i.e. a Ajax request is pending. + * @returns {boolean} + */ + isLoading: function() { + return !!this._isLoading; + }, + /* + * @deprecated since v2.4.0: Use isRootNode() instead + */ + isRoot: function() { + return this.isRootNode(); + }, + /** Return true if node is partially selected (tri-state). + * @returns {boolean} + * @since 2.23 + */ + isPartsel: function() { + return !this.selected && !!this.partsel; + }, + /** (experimental) Return true if this is partially loaded. + * @returns {boolean} + * @since 2.15 + */ + isPartload: function() { + return !!this.partload; + }, + /** Return true if this is the (invisible) system root node. + * @returns {boolean} + * @since 2.4 + */ + isRootNode: function() { + return this.tree.rootNode === this; + }, + /** Return true if node is selected, i.e. has a checkmark set (see also FancytreeNode#isActive). + * @returns {boolean} + */ + isSelected: function() { + return !!this.selected; + }, + /** Return true if this node is a temporarily generated system node like + * 'loading', 'paging', or 'error' (node.statusNodeType contains the type). + * @returns {boolean} + */ + isStatusNode: function() { + return !!this.statusNodeType; + }, + /** Return true if this node is a status node of type 'paging'. + * @returns {boolean} + * @since 2.15 + */ + isPagingNode: function() { + return this.statusNodeType === "paging"; + }, + /** Return true if this a top level node, i.e. a direct child of the (invisible) system root node. + * @returns {boolean} + * @since 2.4 + */ + isTopLevel: function() { + return this.tree.rootNode === this.parent; + }, + /** Return true if node is lazy and not yet loaded. For non-lazy nodes always return false. + * @returns {boolean} + */ + isUndefined: function() { + return this.hasChildren() === undefined; // also checks if the only child is a status node + }, + /** Return true if all parent nodes are expanded. Note: this does not check + * whether the node is scrolled into the visible part of the screen. + * @returns {boolean} + */ + isVisible: function() { + var i, + l, + n, + hasFilter = this.tree.enableFilter, + parents = this.getParentList(false, false); + + // TODO: check $(n.span).is(":visible") + // i.e. return false for nodes (but not parents) that are hidden + // by a filter + if (hasFilter && !this.match && !this.subMatchCount) { + // this.debug( "isVisible: HIDDEN (" + hasFilter + ", " + this.match + ", " + this.match + ")" ); + return false; + } + + for (i = 0, l = parents.length; i < l; i++) { + n = parents[i]; + + if (!n.expanded) { + // this.debug("isVisible: HIDDEN (parent collapsed)"); + return false; + } + // if (hasFilter && !n.match && !n.subMatchCount) { + // this.debug("isVisible: HIDDEN (" + hasFilter + ", " + this.match + ", " + this.match + ")"); + // return false; + // } + } + // this.debug("isVisible: VISIBLE"); + return true; + }, + /** Deprecated. + * @deprecated since 2014-02-16: use load() instead. + */ + lazyLoad: function(discard) { + $.error( + "FancytreeNode.lazyLoad() is deprecated since 2014-02-16. Use .load() instead." + ); + }, + /** + * Load all children of a lazy node if neccessary. The expanded state is maintained. + * @param {boolean} [forceReload=false] Pass true to discard any existing nodes before. Otherwise this method does nothing if the node was already loaded. + * @returns {$.Promise} + */ + load: function(forceReload) { + var res, + source, + self = this, + wasExpanded = this.isExpanded(); + + _assert(this.isLazy(), "load() requires a lazy node"); + // _assert( forceReload || this.isUndefined(), "Pass forceReload=true to re-load a lazy node" ); + if (!forceReload && !this.isUndefined()) { + return _getResolvedPromise(this); + } + if (this.isLoaded()) { + this.resetLazy(); // also collapses + } + // This method is also called by setExpanded() and loadKeyPath(), so we + // have to avoid recursion. + source = this.tree._triggerNodeEvent("lazyLoad", this); + if (source === false) { + // #69 + return _getResolvedPromise(this); + } + _assert( + typeof source !== "boolean", + "lazyLoad event must return source in data.result" + ); + res = this.tree._callHook("nodeLoadChildren", this, source); + if (wasExpanded) { + this.expanded = true; + res.always(function() { + self.render(); + }); + } else { + res.always(function() { + self.renderStatus(); // fix expander icon to 'loaded' + }); + } + return res; + }, + /** Expand all parents and optionally scroll into visible area as neccessary. + * Promise is resolved, when lazy loading and animations are done. + * @param {object} [opts] passed to `setExpanded()`. + * Defaults to {noAnimation: false, noEvents: false, scrollIntoView: true} + * @returns {$.Promise} + */ + makeVisible: function(opts) { + var i, + self = this, + deferreds = [], + dfd = new $.Deferred(), + parents = this.getParentList(false, false), + len = parents.length, + effects = !(opts && opts.noAnimation === true), + scroll = !(opts && opts.scrollIntoView === false); + + // Expand bottom-up, so only the top node is animated + for (i = len - 1; i >= 0; i--) { + // self.debug("pushexpand" + parents[i]); + deferreds.push(parents[i].setExpanded(true, opts)); + } + $.when.apply($, deferreds).done(function() { + // All expands have finished + // self.debug("expand DONE", scroll); + if (scroll) { + self.scrollIntoView(effects).done(function() { + // self.debug("scroll DONE"); + dfd.resolve(); + }); + } else { + dfd.resolve(); + } + }); + return dfd.promise(); + }, + /** Move this node to targetNode. + * @param {FancytreeNode} targetNode + * @param {string} mode
    +		 *      'child': append this node as last child of targetNode.
    +		 *               This is the default. To be compatble with the D'n'd
    +		 *               hitMode, we also accept 'over'.
    +		 *      'firstChild': add this node as first child of targetNode.
    +		 *      'before': add this node as sibling before targetNode.
    +		 *      'after': add this node as sibling after targetNode.
    + * @param {function} [map] optional callback(FancytreeNode) to allow modifcations + */ + moveTo: function(targetNode, mode, map) { + if (mode === undefined || mode === "over") { + mode = "child"; + } else if (mode === "firstChild") { + if (targetNode.children && targetNode.children.length) { + mode = "before"; + targetNode = targetNode.children[0]; + } else { + mode = "child"; + } + } + var pos, + tree = this.tree, + prevParent = this.parent, + targetParent = + mode === "child" ? targetNode : targetNode.parent; + + if (this === targetNode) { + return; + } else if (!this.parent) { + $.error("Cannot move system root"); + } else if (targetParent.isDescendantOf(this)) { + $.error("Cannot move a node to its own descendant"); + } + if (targetParent !== prevParent) { + prevParent.triggerModifyChild("remove", this); + } + // Unlink this node from current parent + if (this.parent.children.length === 1) { + if (this.parent === targetParent) { + return; // #258 + } + this.parent.children = this.parent.lazy ? [] : null; + this.parent.expanded = false; + } else { + pos = $.inArray(this, this.parent.children); + _assert(pos >= 0, "invalid source parent"); + this.parent.children.splice(pos, 1); + } + // Remove from source DOM parent + // if(this.parent.ul){ + // this.parent.ul.removeChild(this.li); + // } + + // Insert this node to target parent's child list + this.parent = targetParent; + if (targetParent.hasChildren()) { + switch (mode) { + case "child": + // Append to existing target children + targetParent.children.push(this); + break; + case "before": + // Insert this node before target node + pos = $.inArray(targetNode, targetParent.children); + _assert(pos >= 0, "invalid target parent"); + targetParent.children.splice(pos, 0, this); + break; + case "after": + // Insert this node after target node + pos = $.inArray(targetNode, targetParent.children); + _assert(pos >= 0, "invalid target parent"); + targetParent.children.splice(pos + 1, 0, this); + break; + default: + $.error("Invalid mode " + mode); + } + } else { + targetParent.children = [this]; + } + // Parent has no