index 1fef153b638bc87723acf6a0508d7f0c0725b73e..b6ef2a6125cae94dd430be037d543df5d9b1cd45 100644 (file)
/* TEST PHASE .... */
/* TEST PHASE .... */
+ static function get_all_objects_for_given_object($dn,$filter,$detailed = false)
+ {
+ $res = FAI::get_all_objects_for_given_base($dn,$filter,$detailed);
+ $name = preg_replace("/,ou=.*$/","",$dn);
+ $entries = array();
+ foreach($res as $entry_dn => $data){
+ if(!preg_match("/,".$name.",/",$entry_dn)) continue;
+ $entries[$entry_dn] = $data;
+ }
+ return($entries);
+ }
+
+
+
+
/* Returns all object for the given release.
This function resolves the releases
from base up to the given dn.
/* Returns all object for the given release.
This function resolves the releases
from base up to the given dn.
$tmp = array();
if(!FAI::is_release_department($Current_DN)) {
$tmp = array();
if(!FAI::is_release_department($Current_DN)) {
- return($res);
+# return($res);
}
/* Collect some basic informations and initialize some variables */
$base_release = FAI::get_release_dn($Current_DN);
}
/* Collect some basic informations and initialize some variables */
$base_release = FAI::get_release_dn($Current_DN);
- $previous_releases = array_reverse(FAI:: get_previous_releases_of_this_release($base_release,true));
+ $previous_releases = array_reverse(FAI::get_previous_releases_of_this_release($base_release,true));
+
+ $ldap->cat($base_release);
+ $attrs = $ldap->fetch();
+ $FAIstate = "branch";
+ if(isset($attrs['FAIstate'][0])){
+ $FAIstate = $attrs['FAIstate'][0];
+ }
/* We must also include the given release dn */
$previous_releases[] = $base_release;
/* We must also include the given release dn */
$previous_releases[] = $base_release;
foreach($deps_to_search as $fai_base){
/* Ldap search for fai classes specified in this release */
foreach($deps_to_search as $fai_base){
/* Ldap search for fai classes specified in this release */
- $attributes = array("dn","objectClass","FAIstate","cn");
- $res_tmp = get_list($filter,"fai",$fai_base,$attributes,GL_SUBSEARCH | GL_SIZELIMIT);
+ $attributes = array("dn","objectClass","FAIstate","cn","FAIdiskType","FAIlvmDevice","FAIdiskOption");
+ $res_tmp = get_list($filter,"fai",$fai_base,$attributes,GL_SUBSEARCH);
/* check the returned objects, and add/replace them in our return variable */
foreach($res_tmp as $attr){
$buffer = array();
/* check the returned objects, and add/replace them in our return variable */
foreach($res_tmp as $attr){
$buffer = array();
- $name = preg_replace("/".normalizePreg($release)."/i","",$attr['dn']);
+ $name = preg_replace("/".preg_quote($release, '/')."/i","",$attr['dn']);
if(isset($attr['FAIstate'][0])){
if(preg_match("/removed$/",$attr['FAIstate'][0])){
if(isset($attr['FAIstate'][0])){
if(preg_match("/removed$/",$attr['FAIstate'][0])){
}
}
}
}
+ /* Seems to be an inherited class, apply current FAIstate to this classes
+ */
+ if(!preg_match("/".preg_quote($base_release, '/')."$/i",$attr['dn'])){
+ $buffer['FAIstate'] = $FAIstate;
+ }else{
+
+ /* Seems to be created within this release department.
+ This indicates - it can't be of state "freeze"
+ */
+ if(isset($attr['FAIstate'])){
+ $buffer['FAIstate'] = $attr['FAIstate'][0];
+ }else{
+ $buffer['FAIstate'] = "branch";
+ }
+ }
+
+ if(isset($attr['FAIdiskType'])){
+ $buffer['FAIdiskType'] = $attr['FAIdiskType'][0];
+ } else {
+ $buffer['FAIdiskType'] = "old";
+ }
+
+ if(isset($attr['FAIdiskOption'])){
+ $buffer['FAIdiskOption'] = $attr['FAIdiskOption'];
+ } else {
+ $buffer['FAIdiskOption'] = null;
+ }
+ if(isset($attr['FAIlvmDevice'])){
+ $buffer['FAIlvmDevice'] = $attr['FAIlvmDevice'];
+ }
+
/* In detailed mode are some additonal informations visible */
if($detailed){
/* In detailed mode are some additonal informations visible */
if($detailed){
{
global $config;
{
global $config;
- if(!preg_match("/".normalizePreg(get_ou('faiou'))."/",$dn)){
- $base = get_ou('faiou').$dn;
+ if(!preg_match("/".preg_quote(get_ou('faiBaseRDN'), '/')."/i",$dn)){
+ $base = get_ou('faiBaseRDN').$dn;
}else{
$base = $dn;
}
}else{
$base = $dn;
}
$ldap->search("(objectClass=FAIbranch)",array("ou","dn"));
while($attrs = $ldap->fetch()){
if($appendedName){
$ldap->search("(objectClass=FAIbranch)",array("ou","dn"));
while($attrs = $ldap->fetch()){
if($appendedName){
- $res[$attrs['dn']] = convert_department_dn(preg_replace("/,".normalizePreg(get_ou('faiou')).".*$/","",$attrs['dn']));
+ $res[$attrs['dn']] = convert_department_dn(preg_replace("/,".preg_quote(get_ou('faiBaseRDN'), '/').".*$/i","",$attrs['dn']));
}else{
$res[$attrs['dn']] = $attrs['ou'][0];
}
}else{
$res[$attrs['dn']] = $attrs['ou'][0];
}
/* Dont't try to modify non FAIclasses
*/
/* Dont't try to modify non FAIclasses
*/
- if(!preg_match("/[^,]+,".normalizePreg(get_ou("faiou"))."/",$obj_dn)){
- echo "PLEASE check fai class handling in ".__LINE__." -> ".__FILE__;
+ if(!preg_match("/[^,]+,".preg_quote(get_ou("faiBaseRDN"), '/')."/i",$obj_dn)){
+ trigger_error("PLEASE check fai class handling in ".__LINE__." -> ".__FILE__);
+ echo "<br>-->".$Current_DN."<br>";
+ echo "<br>-->".$obj_dn."<br>";
}else{
/* Get source object and check if it is a base FAIclass
*/
$ldap->cat($obj_dn);
$attrs = $ldap->fetch();
}else{
/* Get source object and check if it is a base FAIclass
*/
$ldap->cat($obj_dn);
$attrs = $ldap->fetch();
- $classes = array("FAIprofile","FAIscript","FAIpackage","FAIpartitionTable","FAIHook","FAIvariable","FAItemplate");
+ $classes = array("FAIprofile","FAIscript","FAIpackageList","FAIpartitionTable","FAIHook","FAIvariable","FAItemplate");
if(count(array_intersect($classes,$attrs['objectClass']))){
$cn = $attrs['cn'][0];
if(count(array_intersect($classes,$attrs['objectClass']))){
$cn = $attrs['cn'][0];
$classes = FAI::get_all_objects_for_given_base(FAI::get_release_dn($Current_DN),
"(&(objectClass=FAIclass)(cn=".$cn."))");
$classes = FAI::get_all_objects_for_given_base(FAI::get_release_dn($Current_DN),
"(&(objectClass=FAIclass)(cn=".$cn."))");
- /* Check if this is the last class with this name.
- */
+ /* Check if this is the last class with this name */
if(count($classes) == 1){
/* Get all FAI Profiles
if(count($classes) == 1){
/* Get all FAI Profiles
/* Check if this Profile uses the source class ($cn)
*/
/* Check if this Profile uses the source class ($cn)
*/
- if(preg_match("/".normalizePreg($cn)."/",$attrs['FAIclass'])){
- $attrs['FAIclass'] = preg_replace("/[ ]*".normalizePreg($cn)."[ ]*/i"," ",$attrs['FAIclass']);
+ if(preg_match("/".preg_quote($cn, '/')."/",$attrs['FAIclass'])){
+ $attrs['FAIclass'] = preg_replace("/[ ]*".preg_quote($cn, '/')."[ ]*/i"," ",$attrs['FAIclass']);
if(empty($attrs['FAIclass'])){
$attrs['FAIclass'] = array();
}
if(empty($attrs['FAIclass'])){
$attrs['FAIclass'] = array();
}
{
foreach($ar1 as $ak1 => $av1){
if(!isset($ar2[$ak1]) || (!($av1 === $ar2[$ak1]))){
{
foreach($ar1 as $ak1 => $av1){
if(!isset($ar2[$ak1]) || (!($av1 === $ar2[$ak1]))){
- return(true);
+ return(TRUE);
}elseif(is_array($av1)){
}elseif(is_array($av1)){
- return(FAI::arr_diff($av1,$ar2[$ak1]));
+ $ret = (FAI::arr_diff($av1,$ar2[$ak1]));
+ if($ret) {
+ return(TRUE);
+ }
}
}
return(FALSE);
}
}
return(FALSE);
/* check which objects must be saved, and save them */
static function save_release_changes_now()
{
/* check which objects must be saved, and save them */
static function save_release_changes_now()
{
+ global $config;
/* Variable init*/
$to_save = array();
/* Variable init*/
$to_save = array();
+
+ $reload_fai_classes = FALSE;
/* check which objects must be saved */
/* check which objects must be saved */
+ if(!session::is_set('FAI_objects_to_save')){
+ return;
+ }
$FAI_objects_to_save = session::get('FAI_objects_to_save');
if(!is_array($FAI_objects_to_save)) {
$FAI_objects_to_save = session::get('FAI_objects_to_save');
if(!is_array($FAI_objects_to_save)) {
+ print_a(array(session::get('FAI_objects_to_save')));
trigger_error("Can't save FAI objects, no array given.");
return;
}
trigger_error("Can't save FAI objects, no array given.");
return;
}
+
foreach($FAI_objects_to_save as $Current_DN => $object){
if($object['diff']){
$sub_name = $Current_DN;
foreach($FAI_objects_to_save as $Current_DN => $object){
if($object['diff']){
$sub_name = $Current_DN;
}
$to_save = $tmp;
}
$to_save = $tmp;
+
/* Save objects and manage the correct release behavior*/
foreach($to_save as $save){
/* Save objects and manage the correct release behavior*/
foreach($to_save as $save){
$objectAttrs= $save['objectAttrs'];
/* Get ldap object */
$objectAttrs= $save['objectAttrs'];
/* Get ldap object */
- global $config;
$ldap = $config->get_ldap_link();
$ldap->cd($config->current['BASE']);
/* Get some basic informations */
$base_release = FAI::get_release_dn($Current_DN);
$ldap = $config->get_ldap_link();
$ldap->cd($config->current['BASE']);
/* Get some basic informations */
$base_release = FAI::get_release_dn($Current_DN);
- $sub_releases = FAI:: get_sub_releases_of_this_release($base_release,true);
+ $sub_releases = FAI::get_sub_releases_of_this_release($base_release,true);
$parent_obj = FAI::get_parent_release_object($Current_DN);
$parent_obj = FAI::get_parent_release_object($Current_DN);
- $following_releases = FAI:: get_sub_releases_of_this_release($base_release,true);
+ $following_releases = $sub_releases;
/* Check if given dn exists or if is a new entry */
$ldap->cat($Current_DN);
/* Check if given dn exists or if is a new entry */
$ldap->cat($Current_DN);
}else{
$objectAttrs['FAIstate'][0] = "|removed";
}
}else{
$objectAttrs['FAIstate'][0] = "|removed";
}
+
+ /* Force reload of FAI classes */
+ $classes = array("FAIprofile","FAIscript","FAIpackageList","FAIpartitionTable","FAIHook","FAIvariable","FAItemplate");
+ if(count(array_intersect($classes,$objectAttrs['objectClass']))){
+ $reload_fai_classes = TRUE;
+ }
}
}
}
}
print_a($objectAttrs);
}
FAI::save_FAI_object($Current_DN,$objectAttrs);
print_a($objectAttrs);
}
FAI::save_FAI_object($Current_DN,$objectAttrs);
+
+ /* Force reload of FAI classes */
+ $classes = array("FAIprofile","FAIscript","FAIpackageList","FAIpartitionTable","FAIHook","FAIvariable","FAItemplate");
+ if(count(array_intersect($classes,$objectAttrs['objectClass']))){
+ $reload_fai_classes = TRUE;
+ }
+
}else{
/* This object is inherited by some sub releases */
}else{
/* This object is inherited by some sub releases */
/* New objects require special handling */
if($is_new){
/* New objects require special handling */
if($is_new){
+ /* Force reload of FAI classes */
+ $classes = array("FAIprofile","FAIscript","FAIpackageList","FAIpartitionTable","FAIHook","FAIvariable","FAItemplate");
+ if(count(array_intersect($classes,$objectAttrs['objectClass']))){
+ $reload_fai_classes = TRUE;
+ }
+
/* check if there is already an entry named like this,
in one of our parent releases */
if(!empty($parent_obj)){
/* check if there is already an entry named like this,
in one of our parent releases */
if(!empty($parent_obj)){
/* check if we must patch the follwing release */
if(!empty($r)){
/* check if we must patch the follwing release */
if(!empty($r)){
+
foreach($r as $key ){
if(DEBUG_FAI_FUNC) {
echo "<b>Copy current objects original attributes to next release</b> ".$key;
print_a($parent_attrs);
}
foreach($r as $key ){
if(DEBUG_FAI_FUNC) {
echo "<b>Copy current objects original attributes to next release</b> ".$key;
print_a($parent_attrs);
}
+
+ /* Append FAIstate tag to ensure that freezed objects stay freezed
+ */
+ $rTag = FAI::get_release_tag(FAI::get_release_dn($key));
+ $parent_attrs['FAIstate'] = $rTag;
+
+ /* FAItemplateFile can be binary, therefore it needs to be fetched with
+ * $ldap->get_attribute */
+ if (isset($parent_attrs['FAItemplateFile'])) {
+ $parent_attrs['FAItemplateFile'] = $ldap->get_attribute($parent_obj, 'FAItemplateFile');
+ }
+
FAI::save_FAI_object($key,$parent_attrs);
}
}
FAI::save_FAI_object($key,$parent_attrs);
}
}
}
}
}
}
}
}
- }
+ }
+
+ /* Reload GOsa si FAI DB/cache
+ */
+ if($reload_fai_classes){
+ if( class_available("DaemonEvent") && class_available("gosaSupportDaemon")){
+ $events = DaemonEvent::get_event_types(SYSTEM_EVENT | HIDDEN_EVENT);
+ if(isset($events['TRIGGERED']['DaemonEvent_recreate_fai_release_db'])){
+ $evt = $events['TRIGGERED']['DaemonEvent_recreate_fai_release_db'];
+ $tmp = new $evt['CLASS_NAME']($config);
+ $tmp->set_type(TRIGGERED_EVENT);
+ $tmp->add_targets(array("GOSA"));
+ $o_queue = new gosaSupportDaemon();
+ if(!$o_queue->append($tmp)){
+ msg_dialog::display(_("Service infrastructure"),msgPool::siError($o_queue->get_error()),ERROR_DIALOG);
+ }
+ }
+ }
+ }
+
session::set('FAI_objects_to_save',array());
}
session::set('FAI_objects_to_save',array());
}
$buffer = array();
# $name = str_ireplace($release,"",$attr['dn']);
$buffer = array();
# $name = str_ireplace($release,"",$attr['dn']);
- $name = preg_replace("/".normalizePreg($release)."/i","",$attr['dn']);
+ $name = preg_replace("/".preg_quote($release, '/')."/i","",$attr['dn']);
if(isset($attr['FAIstate'][0])&&(preg_match("/removed$/",$attr['FAIstate'][0]))){
if(isset($attr['FAIstate'][0])&&(preg_match("/removed$/",$attr['FAIstate'][0]))){
/* Get dn suffix. Example "FAIvairableEntry=keksdose,FAIvariable=Keksregal," */
# $dn_suffix = str_ireplace($base_release,"",$dn);
/* Get dn suffix. Example "FAIvairableEntry=keksdose,FAIvariable=Keksregal," */
# $dn_suffix = str_ireplace($base_release,"",$dn);
- $dn_suffix = preg_replace("/".normalizePreg($base_release)."/i","",$dn);
+ $dn_suffix = preg_replace("/".preg_quote($base_release, '/')."/i","",$dn);
/* Check if given object also exists whitin one of these releases */
foreach($sub_releases as $p_release => $name){
/* Check if given object also exists whitin one of these releases */
foreach($sub_releases as $p_release => $name){
/* Get dn suffix. Example "FAIvairableEntry=keksdose,FAIvariable=Keksregal," */
# $dn_suffix = str_ireplace($base_release,"",$dn);
/* Get dn suffix. Example "FAIvairableEntry=keksdose,FAIvariable=Keksregal," */
# $dn_suffix = str_ireplace($base_release,"",$dn);
- $dn_suffix = preg_replace("/".normalizePreg($base_release)."/i","",$dn);
+ $dn_suffix = preg_replace("/".preg_quote($base_release, '/')."/i","",$dn);
/* Check if given object also exists whitin one of these releases */
foreach($previous_releases as $p_release){
/* Check if given object also exists whitin one of these releases */
foreach($previous_releases as $p_release){
$ret = array();
/* Explode dns into pieces, to be able to build parent dns */
$ret = array();
/* Explode dns into pieces, to be able to build parent dns */
- $dns_to_check = gosa_ldap_explode_dn(preg_replace("/".normalizePreg(",".$config->current['BASE'])."/i","",$dn));
+ $dns_to_check = gosa_ldap_explode_dn(preg_replace("/".preg_quote(",".$config->current['BASE'], '/')."/i","",$dn));
if(!is_array($dns_to_check)){
return;
if(!is_array($dns_to_check)){
return;
$start = microtime(TRUE);
$source_release = trim($source_release,"/");
$start = microtime(TRUE);
$source_release = trim($source_release,"/");
- echo "<h2>".sprintf(_("Creating group application release for %s"),$destination_name)."</h2>";
+ echo "<h3>".sprintf(_("Creating group application release for %s"),$destination_name)."</h3>";
$sub_releases = array();
$source_dn = "";
$sub_releases = array();
$source_dn = "";
- $tmp = split("\/",$source_release);
+ $tmp = explode("/",$source_release);
foreach($tmp as $part){
if(empty($part)){
continue;
foreach($tmp as $part){
if(empty($part)){
continue;
$ldap->search("(objectClass=FAIbranch)",array("ou","FAIstate"));
while($attrs = $ldap->fetch()){
foreach($sub_releases as $sub_rel){
$ldap->search("(objectClass=FAIbranch)",array("ou","FAIstate"));
while($attrs = $ldap->fetch()){
foreach($sub_releases as $sub_rel){
- if(preg_match("/^".normalizePreg($sub_rel.get_ou('faiou'))."/",$attrs['dn'])){
- $f_releases[$sub_rel.get_ou('faiou')] = $attrs;
+ if(preg_match("/^".preg_quote($sub_rel.get_ou('faiBaseRDN'), '/')."/i",$attrs['dn'])){
+ $f_releases[$sub_rel.get_ou('faiBaseRDN')] = $attrs;
}
}
}
}
}
}
foreach($sub_releases as $name => $sub_rel){
$FAIstate = "";
foreach($sub_releases as $name => $sub_rel){
$FAIstate = "";
- if(isset($f_releases[$sub_rel.get_ou('faiou')]) && isset($f_releases[$sub_rel.get_ou('faiou')]['FAIstate'])){
- $FAIstate = $f_releases[$sub_rel.get_ou('faiou')]['FAIstate'][0];
+ if(isset($f_releases[$sub_rel.get_ou('faiBaseRDN')]) && isset($f_releases[$sub_rel.get_ou('faiBaseRDN')]['FAIstate'])){
+ $FAIstate = $f_releases[$sub_rel.get_ou('faiBaseRDN')]['FAIstate'][0];
}
foreach($groups as $dn => $data){
}
foreach($groups as $dn => $data){
}else{
$source_dns = array();
foreach($g_releases as $dn => $data){
}else{
$source_dns = array();
foreach($g_releases as $dn => $data){
- if(preg_match("/^".normalizePreg($source_dn)."/",$dn)){
+ if(preg_match("/^".preg_quote($source_dn, '/')."/",$dn)){
$source_dns[$dn] = $data;
}
}
$source_dns[$dn] = $data;
}
}
$to_copy = array();
foreach($source_dns as $dn => $attrs){
$ldap->cd($dn);
$to_copy = array();
foreach($source_dns as $dn => $attrs){
$ldap->cd($dn);
- $ldap->ls("(objectClass=gotoSubmenuEntry)(objectClass=gotoMenuEntry)",$dn,array("dn"));
+ $ldap->ls("(|(objectClass=gotoSubmenuEntry)(objectClass=gotoMenuEntry))",$dn,array("dn"));
while($attrs = $ldap->fetch()){
while($attrs = $ldap->fetch()){
- $destination = preg_replace("/".normalizePreg($dn)."$/","ou=".$destination_name.",".$dn,$attrs['dn']);
+ $destination = preg_replace("/".preg_quote($dn, '/')."$/","ou=".$destination_name.",".$dn,$attrs['dn']);
$to_copy[$attrs['dn']] = $destination;
}
}
$to_copy[$attrs['dn']] = $destination;
}
}
* @param $is_first Boolean Use to identify the first func. call when recursivly called.
* @param $depth Integer Current depth of recursion.
*/
* @param $is_first Boolean Use to identify the first func. call when recursivly called.
* @param $depth Integer Current depth of recursion.
*/
- function copy_FAI_resource_recursive($sourcedn,$destinationdn,$destinationName,$type="branch",$is_first = true,$depth=0)
+ static function copy_FAI_resource_recursive($sourcedn,$destinationdn,$destinationName,$type="branch",$is_first = true,$depth=0)
{
global $config;
error_reporting(E_ALL | E_STRICT);
{
global $config;
error_reporting(E_ALL | E_STRICT);
/* Display current action information.
*/
if($is_first){
/* Display current action information.
*/
if($is_first){
- echo "<h2>".sprintf(_("Creating copy of %s"),"<i>".LDAP::fix($sourcedn)."</i>")."</h2>";
+ echo "<h3>".sprintf(_("Creating copy of %s"),"<i>".LDAP::fix($sourcedn)."</i>")."</h3>";
}else{
if(preg_match("/^ou=/",$sourcedn)){
echo "<h3>"._("Processing")." <i>".LDAP::fix($destinationdn)."</i></h3>";
}else{
}else{
if(preg_match("/^ou=/",$sourcedn)){
echo "<h3>"._("Processing")." <i>".LDAP::fix($destinationdn)."</i></h3>";
}else{
- $tmp = split(",",$sourcedn);
+ $tmp = explode(",",$sourcedn);
echo " <b>"._("Object").":</b> ";
$deststr = LDAP::fix($destinationdn);
if(strlen($deststr) > 96){
echo " <b>"._("Object").":</b> ";
$deststr = LDAP::fix($destinationdn);
if(strlen($deststr) > 96){
/* Add marking attribute */
$attr['objectClass'][] = "FAIbranch";
/* Add marking attribute */
$attr['objectClass'][] = "FAIbranch";
+ $attr['FAIstate'] = $type;
/* Add this entry */
$ldap->modify($attr);
/* Add this entry */
$ldap->modify($attr);
}
unset($attr['count']);
unset($attr['dn']);
}
unset($attr['count']);
unset($attr['dn']);
+ if(!in_array("FAIobject",$attr['objectClass'])){
+ $attr['objectClass'][] = "FAIobject";
+ }
+ $attr['FAIstate'] = $type;
/* Add entry
*/
/* Add entry
*/
if(!$ldap->success()){
/* Some error occurred */
if(!$ldap->success()){
/* Some error occurred */
- print "---------------------------------------------";
- print $ldap->get_error()."<br>";
- print $sourcedn."<br>";
- print $destinationdn."<br>";
- print_a( $attr);
+ msg_dialog::display(_("Fatal error"),
+ sprintf(_("Release creation failed due to ldap errors. Additional informations '%s'."),
+ $ldap->get_error()."<br>".$sourcedn."<br>".$destinationdn."<br>"),FATAL_ERROR_DIALOG);
exit();
}
}
exit();
}
}
}
}
if($is_first){
}
}
if($is_first){
- echo "<p class='seperator'> </p>";
+ echo "<hr>";
}
}
}
}
$ldap->cd($config->current['BASE']);
/* Split dn into pices */
$ldap->cd($config->current['BASE']);
/* Split dn into pices */
- $dns_to_check = gosa_ldap_explode_dn(preg_replace("/".normalizePreg(",".$config->current['BASE'])."/i","",$Current_DN));
+ $dns_to_check = gosa_ldap_explode_dn(preg_replace("/".preg_quote(",".$config->current['BASE'], '/')."/i","",$Current_DN));
if(!is_array($dns_to_check)){
return;
if(!is_array($dns_to_check)){
return;