#!/bin/sh  
# \
  exec oagwish "$0" "$@"


#
# Input file: /home/helios/OAG/FACLsetup.sdds
#

set auto_path [linsert $auto_path 0  /usr/local/oag/apps/lib/$env(HOST_ARCH)]
set auto_path [linsert $auto_path 0 /usr/local/oag/lib_patch/$env(HOST_ARCH)]

set CVSRevisionAuthor "\$Revision: 1.22 $ \$Author: soliday $"
set apsttk 1

# runs the actual setfacl commands
proc setACL {filename acls} {
    global Verbose
    if [catch {os setfacl $acls $filename} result] {
        if [file isdirectory $filename] {
            .userFrame.status.frame.fg.top.text insert end "error: $result\n" RED_FG
            .userFrame.status.frame.fg.top.text insert end "command: setfacl -s $acls $filename. " RED_FG
            .userFrame.status.frame.fg.top.text insert end "Owner is [file attributes $filename -owner].\n"
            #  bell
            return
        }
        set tmpName /tmp/[APSTmpString]
        if {[catch {file copy $filename $tmpName} result]} {
            .userFrame.status.frame.fg.top.text insert end "error: $result. " RED_FG
            .userFrame.status.frame.fg.top.text insert end "Owner is [file attributes $filename -owner].\n" 
            return
        }
        if {[catch {file delete -force $filename} result]} {
            .userFrame.status.frame.fg.top.text insert end "error: $result. " RED_FG
            .userFrame.status.frame.fg.top.text insert end "Owner is [file attributes $filename -owner].\n" 
            #   bell
            file delete -force $tmpName
            return
        }
        if {[catch {file rename $tmpName $filename} result]} {
            puts "error: $result"
            exit
        }
        if [catch {os setfacl  $acls $filename} result] {
            .userFrame.status.frame.fg.top.text insert end "error: $result\n" RED_FG
            .userFrame.status.frame.fg.top.text insert end "command: setfacl -s $acls $filename. " RED_FG
            .userFrame.status.frame.fg.top.text insert end "Owner is [file attributes $filename -owner].\n"
            #   bell
        }
    }
    if { $Verbose } {
        APSSetVarAndUpdate status "setfacl for $filename"
    }
   
}

proc setACL_NFS4 {filenames users isdirectory} {
    global Verbose stopNow tcl_platform
    set owned 1
    if { $Verbose } {
	foreach filename $filenames {
	    APSSetVarAndUpdate status "setting ACLs for $filename"
	}
    }
    foreach filename $filenames {
        if {![file owned $filename]} {
            set owned 0
            break
        }
    }
    foreach filename $filenames {
        if {[file extension $filename] == ".FACL_NEW"} {
            puts "Found: $filename"
            puts "This should not be here."
            exit
        }
        if {[file extension $filename] == ".FACL_OLD"} {
            puts "Found: $filename"
        }
    }

    if {$owned == 0} {
        if {[catch {exec chown oag:oagmgr $filenames} result]} {
	    set copyFiles 0
	    if {$tcl_platform(os) == "Linux"} {
		set copyFiles 1
	    } else {
		if {[catch {eval exec /usr/bin/chmod A- $filenames} result]} {
		    set copyFiles 1
		}
	    }
	    if {$copyFiles} {
		set fnlist ""
		foreach filename $filenames {
		    APSSetVarAndUpdate status "setting ACLs for $filename"
		    if {[file extension $filename] == ".FACL_OLD"} {
			continue
		    }
		    set copyFile 0
		    if {$tcl_platform(os) == "Linux"} {
			set copyFile 1
		    } else {
			if {[catch {eval exec /usr/bin/chmod A- $filename} result]} {
			    set copyFile 1
			}
		    }
		    if {$copyFile} {
			if {$isdirectory} {
			    if {$tcl_platform(os) == "Linux"} {
				set copy /bin/cp
				set move /bin/mv
				set remove /bin/rm
			    } else {
				set copy /opt/gnu/bin/cp
				set move /usr/bin/mv
				set remove /usr/bin/rm
			    }
			    if {[catch {exec $copy -dR $filename ${filename}.FACL_NEW} result]} {
				.userFrame.status.frame.fg.top.text insert end "error: $result\n" RED_FG
				#set stopNow 1
				continue
			    }
			    if {[catch {exec $move $filename ${filename}.FACL_OLD} result]} {
				.userFrame.status.frame.fg.top.text insert end "error: $result\n" RED_FG
				puts "error: $result"
				set stopNow 1
				return
			    }
			    if {[catch {exec $move $filename.FACL_NEW ${filename}} result]} {
				.userFrame.status.frame.fg.top.text insert end "error: $result\n" RED_FG
				puts "error: $result"
				set stopNow 1
				return
			    }
			    if {[catch {exec $remove -rf ${filename}.FACL_OLD} result]} {
				.userFrame.status.frame.fg.top.text insert end "error: $result\n" RED_FG
			    }
			    #Don't need to set ACLs on this directory because they are already set.
			} else {
			    
			    set tmpName [file dirname $filename]/[APSTmpString]
			    if {[catch {file copy $filename $tmpName} result]} {
				.userFrame.status.frame.fg.top.text insert end "error: $result. " RED_FG
				.userFrame.status.frame.fg.top.text insert end "Owner is [file attributes $filename -owner].\n" 
				#set stopNow 1
				continue
			    }
			    if {[catch {file delete -force $filename} result]} {
				.userFrame.status.frame.fg.top.text insert end "error: $result. " RED_FG
				.userFrame.status.frame.fg.top.text insert end "Owner is [file attributes $filename -owner].\n" 
				file delete -force $tmpName
				#set stopNow 1
				continue
			    }
			    if {[catch {file rename $tmpName $filename} result]} {
				.userFrame.status.frame.fg.top.text insert end "error: Unable to copy $tmpName to $filename Please copy manually. " RED_FG
				puts "error: $result"
				set stopNow 1
				return
			    }
			    #Don't need to set ACLs on this file because they are already set.
			}
		    } else {
                        lappend fnlist $filename
                    }
		}
		set filenames $fnlist
            }
        }
    }
    if {[llength $filenames] == 0} {return}
    set aclList ""
    if {$tcl_platform(os) == "Linux"} {
	if {$isdirectory} {
	    foreach user $users {
		lappend aclList "A:d:${user}@aps.anl.gov:rwaDdxtTnNcCo"
		lappend aclList "A:fi:${user}@aps.anl.gov:rwaDdxtTnNcCo"
	    }
	    lappend aclList "D::OWNER@:"
	    lappend aclList "A::OWNER@:rwaxTNCo"
	    lappend aclList "D:g:GROUP@:wa"
	    lappend aclList "A:g:GROUP@:rx"
	    lappend aclList "D::EVERYONE@:waTNCo"
	    lappend aclList "A::EVERYONE@:rxtncy"
	    set aclList [join $aclList ,]
	    if {[catch {eval exec nfs4_setfacl -s $aclList $filenames} result]} {
		.userFrame.status.frame.fg.top.text insert end "error: $result\n" RED_FG
		#set stopNow 1
		return
	    }
            if {$filenames == "/net/epics-ops/web_roots/ops/logging/plots"} {
                after 1000
                exec chmod a+w $filenames
                exec chmod g+s $filenames
            }
	} else {
            set execFilenames ""
            set nonexecFilenames ""
            foreach filename $filenames {
                if {[file executable $filename]} {
                    lappend execFilenames $filename
                    puts "executable: $filename"
                } else {
                    lappend nonexecFilenames $filename
                }
            }
            if {[llength $nonexecFilenames]} {
                foreach user $users {
                    lappend aclList "A::${user}@aps.anl.gov:rwaDdtTnNcCo"
                }
                lappend aclList "D::OWNER@:x"
                lappend aclList "A::OWNER@:rwaTNCo"
                lappend aclList "D:g:GROUP@:wax"
                lappend aclList "A:g:GROUP@:r"
                lappend aclList "D::EVERYONE@:waxTNCo"
                lappend aclList "A::EVERYONE@:rtncy"
                set aclList [join $aclList ,]
                if {[catch {eval exec nfs4_setfacl -s $aclList $nonexecFilenames} result]} {
                    .userFrame.status.frame.fg.top.text insert end "error: $result\n" RED_FG
                    set stopNow 1
                    return
                }
            }
            if {[llength $execFilenames]} {
                set aclList ""
                foreach user $users {
                    lappend aclList "A::${user}@aps.anl.gov:rwaDdxtTnNcCo"
                }
                lappend aclList "D::OWNER@:"
                lappend aclList "A::OWNER@:rwxaTNCo"
                lappend aclList "D:g:GROUP@:wa"
                lappend aclList "A:g:GROUP@:rx"
                lappend aclList "D::EVERYONE@:waxTNCo"
                lappend aclList "A::EVERYONE@:rtncy"
                set aclList [join $aclList ,]
                if {[catch {eval exec nfs4_setfacl -s $aclList $execFilenames} result]} {
                    .userFrame.status.frame.fg.top.text insert end "error: $result\n" RED_FG
                    set stopNow 1
                    return
                }
            }
            
	}
    } else {
	if {$isdirectory} {
	    foreach user $users {
		lappend aclList "user:${user}:rwxpdDaARWcCo-:-d----:allow"
		lappend aclList "user:${user}:rwxpdDaARWcCo-:f-i---:allow"
	    }
	    lappend aclList "owner@:--------------:------:deny"
	    lappend aclList "owner@:rwxp---A-W-Co-:------:allow"
	    lappend aclList "group@:-w-p----------:------:deny"
	    lappend aclList "group@:r-x-----------:------:allow"
	    lappend aclList "everyone@:-w-p---A-W-Co-:------:deny"
	    lappend aclList "everyone@:r-x---a-R-c--s:------:allow"
	    set aclList [join $aclList ,]
	    if {[catch {eval exec /usr/bin/chmod A=$aclList $filenames} result]} {
		.userFrame.status.frame.fg.top.text insert end "error: $result\n" RED_FG
		set stopNow 1
		return
	    }
	} else {
	    foreach user $users {
#		lappend aclList "user:${user}:rwxpdDaARWcCo-:------:allow"
		lappend aclList "user:${user}:rw-pdDaARWcCo-:------:allow"
	    }
	    lappend aclList "owner@:--------------:------:deny"
	    #lappend aclList "owner@:rwxp---A-W-Co-:------:allow"
	    lappend aclList "owner@:rw-p---A-W-Co-:------:allow"
	    lappend aclList "group@:-wxp----------:------:deny"
	    lappend aclList "group@:r-------------:------:allow"
	    lappend aclList "everyone@:-wxp---A-W-Co-:------:deny"
	    lappend aclList "everyone@:r-----a-R-c--s:------:allow"
	    set aclList [join $aclList ,]
	    if {[catch {eval exec /usr/bin/chmod A=$aclList $filenames} result]} {
		.userFrame.status.frame.fg.top.text insert end "error: $result\n" RED_FG
		set stopNow 1
		return
	    }
	}
    }
}

# Sets up the acl commands by calling setACL and recursively calling itself.
proc SetFACL {args} {
    global stopNow
    global tcl_platform
    set directory ""
    set users "oag"
    set groups "oagmgr"
    APSStrictParseArguments {directory users groups}

    if {-1 == [lsearch $users oag]} {
        lappend users oag
    }
    foreach user $users {
        lappend acls user:$user:rwx
        lappend dacls defaultuser:$user:rwx
    }
    foreach group $groups {
        lappend acls group:$group:rwx
        lappend dacls defaultgroup:$group:rwx
    }
    lappend acls user::rwx
    lappend acls other:r-x
    lappend acls group::r-x 
    lappend acls group:oagmgr:r-x
    lappend dacls defaultuser::rwx
    lappend dacls defaultother:r-x
    lappend dacls defaultgroup::r-x 
    lappend dacls defaultgroup:oagmgr:r-x

    set files "$directory [glob -nocomplain [file join $directory *]]"

    foreach item $files {
        if {$stopNow} {
            APSSetVarAndUpdate status "Stopping"
            return
        }
        if [string match *.aclOrig $item] continue
        set invalid 0
        while {[string compare [file type $item] link]==0} {
            if {[catch {APSResolveLink $item} item]} {
                .userFrame.status.frame.fg.top.text insert end "Error: Invalid Link: $item\n" RED_FG
                set invalid 1
                break
            }
        }
        if {$invalid} {continue}
        if [file isdirectory $item] {
            if {$item != $directory} {
                # $directory was a link to $item.
                SetFACL -directory $item -users $users -groups $groups
            } else {
	        if { $tcl_platform(os) == "Linux" } {
		   setACL $item [join $acls ,],mask:rwx
		   setACL $item "-d [join $acls ,],mask:rwx"
		} else {   
                   setACL $item [join $acls ,],[join $dacls ,],mask:rwx,defaultmask:rwx
		}   
            }
        } else {
            setACL $item [join $acls ,],mask:rwx
            # file delete -force ${item}.aclOrig
        }
    }
}

proc SetFACL_NFS4 {args} {
    global stopNow dirOnly
    set directory ""
    set users "oag"
    APSStrictParseArguments {directory users}

    if {-1 == [lsearch $users oag]} {
        lappend users oag
    }

    while {[string compare [file type $directory] link]==0} {
	if {[catch {APSResolveLink $directory} directory]} {
	    .userFrame.status.frame.fg.top.text insert end "Error: Invalid Link: $directory\n" RED_FG
	    set invalid 1
	    break
	}
    }

    if {$dirOnly} {
	set files "$directory [glob -types d -nocomplain [file join $directory *]]"
    } else {
	set files "$directory [glob -nocomplain [file join $directory *]]"
    }

    set fileList ""

    foreach item $files {

        if {$stopNow} {
            APSSetVarAndUpdate status "Stopping"
            return
        }
        if [string match *.aclOrig $item] continue
        set invalid 0
        while {[string compare [file type $item] link]==0} {
            if {[catch {APSResolveLink $item} item]} {
                .userFrame.status.frame.fg.top.text insert end "Error: Invalid Link: $item\n" RED_FG
                set invalid 1
                break
            }
        }
        if {$invalid} {continue}
        if [file isdirectory $item] {
            if {$item != $directory} {
		if {($item != "/home/helios14/oagData/sr/IDs/brightnessLog/data") && ($item != "/home/helios/oagData/sr/IDs/brightnessLog/data") && ($item != "/home/helios2/oagData/booster/ramps/autoRampCorrection") && ($item != "/home/helios/oagData/booster/ramps/autoRampCorrection")} {
		    if {[file extension $item] != ".FACL_OLD"} {
			SetFACL_NFS4 -directory $item -users $users
		    }
                    if {[file extension $item] == ".FACL_OLD"} {
                        puts "Found $item"
                    } elseif {[file extension $item] == ".FACL_NEW"} {
                        puts "Found $item"
                    }
		}
            } else {
		setACL_NFS4 $item $users 1
            }
        } else {
	    lappend fileList $item
	    if {[llength $fileList] == 50} {
		if {!$dirOnly} {
		    setACL_NFS4 $fileList $users 0
		}
		set fileList ""
	    }
        }
    }
    if {[llength $fileList]} {
	if {!$dirOnly} {
	    setACL_NFS4 $fileList $users 0
	}
    }
}

proc RunFACL {args} {
    global dirList dirValList dirVal userVal status stopNow
    set stopNow 0
    set i 0
    foreach dir $dirList valueList $dirValList {
        incr i
        if {[set $valueList]} {
            global userList$i userValList$i
            set users ""
            set skip 0
            foreach user [set userList$i] userValueList [set userValList$i] {
                if {[set $userValueList]} {
		    if [ catch {eval exec id $user } result ] {
		      .userFrame.status.frame.fg.top.text insert end "$result\n" RED_FG
		      set skip 1
		    } 
                    lappend users $user
                }
            }
            if {[file exists $dir] == 0} {
                set skip 1
            }
	    if { $skip == 1 } {
	       .userFrame.status.frame.fg.top.text insert end "Skip setting FACL for $dir\n" RED_FG 
	    } else {   
               APSSetVarAndUpdate status "Setting FACL for $dir"
               if {[string first helios $dir] != -1} {
                   set mode NFS4
               } else {
                   #set mode NFS3
                   set mode NFS4
               }
	       if {$mode == "NFS4"} {
		   SetFACL_NFS4 -directory $dir -users $users
	       } else {
		   SetFACL -directory $dir -users $users -groups ""
	       }
	    }   
        }
    }
    APSSetVarAndUpdate status "Done"
}

proc UnselectDirs {args} {
    global dirList dirValList dirVal
    set i 0
    foreach dir $dirList valueList $dirValList {
        incr i
        if {[set $valueList]} {
            set $valueList 0
        }
    }
}
proc SelectDirs {args} {
    global dirList dirValList dirVal
    set i 0
    foreach dir $dirList valueList $dirValList {
        incr i
        set $valueList 1
    }
}

proc AddNewDirectory {args} {
    global newDirectory newUsers newDirResponse status dirList dirWidget dirValList dirs dirVal userVal wparent height
    set newDirResponse 0
    APSDialogBox .add -name "Add New Directory" \
      -okCommand "set newDirResponse ok" -cancelCommand "set newDirResponse cancelled"
    APSLabeledEntry .dir -parent .add.userFrame -label "New directory:" -textVariable newDirectory \
        -width 70 -contextHelp "provide directory to being added."
    APSLabeledEntry .user -parent .add.userFrame -label "User names:" -textVariable newUsers \
      -width 70 -contextHelp "provide user names separate by space or comma"
    
    tkwait variable newDirResponse
    if {$newDirResponse=="cancelled"} {
        APSSetVarAndUpdate status "Adding new directory was cancelled."
        return
    }
    if [lsearch $dirList $newDirectory]>=0 {
        APSSetVarAndUpdate status "$newDirectory already exists in the list"
        return
    }
    if ![file exist $newDirectory] {
        APSSetVarAndUpdate status "$newDirectory does not exist"
        return
    }
    if [regexp {\,} $newUsers] {
        set users [split $newUsers ,]
    } else {
        set users [split $newUsers " "]
    }
    if ![llength $users] {
        APSSetVarAndUpdate status "No users provided for new directory, new directory was not being added."
        return
    }
    incr dirs 
    set i $dirs
    set dirVal($i) 1
    lappend dirList $newDirectory
    lappend dirValList dirVal($i)
    global userList$i userValList$i
    set userList$i ""
    set userValList$i ""
    set j 0
    if [regexp {\,} $newUsers] {
        set users [split $newUsers ,]
    } else {
        set users [split $newUsers " "]
    }
    foreach user $users {
        incr j
        lappend userList$i $user
        set userVal($i,$j) 1
        lappend userValList$i userVal($i,$j)
    }
    
    APSCheckButtonFrame .cbf$i -parent $wparent \
      -label "" \
      -buttonList "$newDirectory" \
      -variableList "dirVal($i)"
    APSButton .add -parent $wparent.cbf$i.frame -text "Add User" -command "AddUser -index $i"
    pack configure $wparent.cbf$i -fill x -pady 5
    #$wparent.cbf$i.frame.button1 configure -padx 4

    APSCheckButtonFrame .cbfgroups$i \
      -parent $wparent.cbf$i.frame \
      -label "" \
      -buttonList "[set userList$i]" \
      -variableList "[set userValList$i]" \
      -orientation "horizontal" \
      -relief flat \
      -limitPerRow 10
    set dirWidget($i) $wparent.cbf$i.frame.cbfgroups$i
    incr height [expr {32 + ($j / 10) * 22}]
    .userFrame.scroll.frame.canvas config -scrollregion "0 0 1000 $height"
    
    if [APSYesNoPopUp "Add the new added directory to databse?"] {
        APSSetVarAndUpdate status "add $newDirectory to database ..."
        set tmpFile /tmp/[APSTmpString]
        if [catch {exec sddsmakedataset $tmpFile -par=Directory,type=string -data=$newDirectory \
                     -col=Users,type=string -data=[join $users ,] } result] {
            return -code error "Unable to make file: $result"
        }
        set newFile [APSNextGenerationedName -name FACLsetup.sdds-0001 -directory /home/helios/OAG/ \
                       -newFile 1 -separator -]
        
        if [catch {exec sddscombine /home/helios/OAG/FACLsetup.sdds $tmpFile -pipe=out \
                     | sddssort -pipe=in -par=Directory /home/helios/OAG/$newFile} result] {
            return -code error "Unable to combine file: $result"
        }
        set oldDir [pwd]
        cd /home/helios/OAG
        catch { exec rm FACLsetup.sdds}
        exec ln -s $newFile FACLsetup.sdds
        cd $oldDir
        APSSetVarAndUpdate status "done"
    }
    
}

proc AddUser {args} {
    set index ""
    APSParseArguments {index}

    global newUsers1 status dirWidget userVal userList$index userValList$index newUserResponse dirList dirVal

    set newUserResponse 0
    APSDialogBox .add -name "Add New Users" \
      -okCommand "set newUserResponse ok" -cancelCommand "set newUserResponse cancelled"
    APSLabeledEntry .dir -parent .add.userFrame -label "New Users :" -textVariable newUsers1 \
        -width 70 -contextHelp "provide news separate by comma or space"
    tkwait variable newUserResponse
    if {$newUserResponse=="cancelled"} {
        APSSetVarAndUpdate status "Adding new users was cancelled."
        return
    }
    if [regexp {\,} $newUsers1] {
        set newList [split $newUsers1 ,]
    } else {
        set newList [split $newUsers1 " "]
    }
    if ![llength $newList] {
        APSSetVarAndUpdate status "No users provided."
        return
    }
    
    set users [set userList$index]
    set j [llength $users]
    set i $index
    set added 0
    foreach user $newList {
        if [lsearch $users $user]<0 {
            incr j
            lappend users $user
            set userVal($i,$j) 1
            lappend userValList$i userVal($i,$j)
            lappend userList$i $user
            set added 1
        }
    }
    if !$added {
        APSSetVarAndUpdate status "The added users already exist, no users added."
        return
    }
    if [info exist dirWidget($i)] {
        destroy $dirWidget($i)
    }
    APSCheckButtonFrame .cbfgroups$i \
      -parent [file root $dirWidget($i)] \
      -label "" \
      -buttonList "[set userList$i]" \
      -variableList "[set userValList$i]" \
      -orientation "horizontal" \
      -relief flat \
      -limitPerRow 10
    APSSetVarAndUpdate status "Updating the database..."
    set tmpRoot /tmp/[APSTmpString]
    set dir [lindex $dirList [expr $i - 1]]
    set dirValue($i) 1
    if [catch {exec sddsprocess /home/helios/OAG/FACLsetup.sdds "-match=par,Directory=$dir,!" $tmpRoot.other
        exec sddsmakedataset $tmpRoot.1 -par=Directory,type=string -data=$dir \
                 -col=Users,type=string -data=[join [set userList$i] ,] 
        exec sddscombine $tmpRoot.other $tmpRoot.1 -pipe=out \
                 | sddssort -pipe=in -par=Directory /home/helios/OAG/FACLsetup.sdds } result] {
        return -code error "Error in adding new users to database: $result"
    }
    file delete -force $tmpRoot.1
    file delete -force $tmpRoot.other
    APSSetVarAndUpdate status "done"
}


set status "Ready..."
APSApplication . -name "Set FACL for OAG directories" -version $CVSRevisionAuthor \
  -overview "This program allows you to set the file access rights for the OAG directories."
APSScrolledStatus .status -parent .userFrame -textVariable status \
  -packOption "-fill both -expand true" -width 115 -height 20
pack configure .userFrame.status.frame -fill both -expand true
.userFrame.status.frame.fg.top.text tag configure RED_FG -foreground red

set args $argv
set allowAllUsers 0
APSStrictParseArguments {allowAllUsers}

if !$allowAllUsers {
    if {$env(USER) != "oag"} {
        APSAlertBox .alert \
          -parent .userFrame \
          -type warning \
          -errorMessage "Warning: You must be oag to run this program"
        exit
    }
}


if {[catch {sdds load /home/helios/OAG/FACLsetup.sdds data} results]} {
    APSAlertBox .alert \
      -parent .userFrame \
      -type error \
      -errorMessage "Error: $results"
    exit
}

set w [APSScroll .scroll -parent .userFrame -packOption "-fill both -expand true"]
.userFrame.scroll.frame.canvas config -yscrollincrement 22 -height 400
pack configure .userFrame.scroll.frame.canvas -fill both

set wparent $w
set dirList ""
set dirValList ""
set i 0
set height 0
foreach dir $data(Parameter.Directory) users $data(Column.Users) {
    incr i
    lappend dirList $dir
    set dirVal($i) 1
    lappend dirValList dirVal($i)

    APSCheckButtonFrame .cbf$i -parent $w \
      -label "" \
      -buttonList "$dir" \
      -variableList "dirVal($i)"
    APSButton .add -parent $w.cbf$i.frame -text "Add User" -command "AddUser -index $i"
    pack configure $w.cbf$i -fill x -pady 5
    #$w.cbf$i.frame.button1 configure -padx 4
    incr height 38

    set userList$i ""
    set userValList$i ""
    set j 0
    foreach user $users {
        incr j
        lappend userList$i $user
        set userVal($i,$j) 1
        lappend userValList$i userVal($i,$j)
    }
    APSCheckButtonFrame .cbfgroups$i \
      -parent $w.cbf$i.frame \
      -label "" \
      -buttonList "[set userList$i]" \
      -variableList "[set userValList$i]" \
      -orientation "horizontal" \
      -relief flat \
      -limitPerRow 10
    set dirWidget($i) $w.cbf$i.frame.cbfgroups$i
    
    incr height [expr {35 + ($j / 10) * 22}]
}
set dirs $i
incr height 10
.userFrame.scroll.frame.canvas config -scrollregion "0 0 1000 $height"

APSCheckButtonFrame .verbose -parent .userFrame -label "" -buttonList "Verbose" -variableList Verbose \
      -orientation "horizontal" -relief flat -packOption "-side right"
APSCheckButtonFrame .dirOnly -parent .userFrame -label "" -buttonList {"Directories Only"} -variableList dirOnly \
      -orientation "horizontal" -relief flat -packOption "-side right"
set Verbose 1
set dirOnly 0  

APSButton .run \
  -parent .userFrame \
  -text "Set FACL" \
  -command "RunFACL"

APSButton .stop \
  -parent .userFrame \
  -text "Stop" \
  -command "set stopNow 1"

APSButton .unselect \
  -parent .userFrame \
  -text "Unselect All Directories" \
  -command "UnselectDirs"

APSButton .select \
  -parent .userFrame \
  -text "Select All Directories" \
  -command "SelectDirs"

APSButton .add \
  -parent .userFrame \
  -text "Add New Directory" \
  -command "AddNewDirectory"

UnselectDirs
