Compare commits
	
		
			23 Commits
		
	
	
		
			11_0_0-9_e
			...
			11_0_0-18_
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| b9f6392c1d | |||
| 659f060eb6 | |||
| 1087a8a5a2 | |||
| 6a5a756438 | |||
| 91ca26de27 | |||
| 8b93232bbe | |||
| ed1c3e13b1 | |||
| 095a0a1499 | |||
| d16ea6a847 | |||
| e70cc67430 | |||
| b097acafab | |||
| 5d7655a709 | |||
| a58df95aa9 | |||
| 251b454470 | |||
| 174d511899 | |||
| 4be8a1411f | |||
| ba431a8a7d | |||
| 77a7849898 | |||
| fb5b0942d6 | |||
| 5edc830f9b | |||
| 473cb57a47 | |||
| 55b85496d4 | |||
| 2fd6f8b630 | 
							
								
								
									
										59
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										59
									
								
								README.md
									
									
									
									
									
								
							@@ -17,6 +17,59 @@ Show list of outstanding bugs: [here](https://bugs.koozali.org/buglist.cgi?compo
 | 
			
		||||
 | 
			
		||||
## Description
 | 
			
		||||
 | 
			
		||||
<br />*This description has been generated by an LLM AI system and cannot be relied on to be fully correct.*
 | 
			
		||||
*Once it has been checked, then this comment will be deleted*
 | 
			
		||||
<br />
 | 
			
		||||
The *smeserver-manager* is a web-based management interface for the SME Server (formerly known as e-smith server and gateway). SME Server is a Linux-based distribution designed for small to medium-sized enterprises, providing a wide range of network services and simplified server management.
 | 
			
		||||
 | 
			
		||||
It is based on the perl Mojolicious package. Mojolicious is a real-time web framework for Perl, which provides a range of functionalities that make it a powerful and flexible tool for web development.
 | 
			
		||||
 | 
			
		||||
### Features
 | 
			
		||||
 | 
			
		||||
#### Web-Based Management Interface:
 | 
			
		||||
smeserver-manager provides an intuitive and user-friendly web interface that allows administrators to manage various aspects of the server without needing deep technical knowledge or command-line skills.
 | 
			
		||||
 | 
			
		||||
#### User and Group Management:
 | 
			
		||||
It allows you to easily add, remove, and manage user accounts and groups. The interface simplifies creating email accounts, setting passwords, and configuring user permissions.
 | 
			
		||||
 | 
			
		||||
#### Network Configuration:
 | 
			
		||||
You can configures network settings such as IP addresses, DNS, DHCP, and gateway settings. The interface also provides options for setting up VPNs, remote access, and firewall rules.
 | 
			
		||||
 | 
			
		||||
#### File Sharing and Storage:
 | 
			
		||||
Enables and manages file sharing services like Samba (for Windows file sharing) and NFS (for Unix/Linux file sharing). Administrators can easily create shared folders and manage permissions.
 | 
			
		||||
 | 
			
		||||
#### Email Services:
 | 
			
		||||
Configure and manage email services, including setting up mail domains, user mailboxes, and SMTP/IMAP/POP3 settings. It also provides options for spam and virus filtering.
 | 
			
		||||
 | 
			
		||||
#### Web Services:
 | 
			
		||||
Host and manage websites using the integrated web server (usually Apache). It supports virtual hosting, where multiple websites can be hosted on the same server.
 | 
			
		||||
 | 
			
		||||
#### Backup and Restore:
 | 
			
		||||
Perform backups of essential data and server configurations. The interface provides options for scheduled backups and restoring from backup files.
 | 
			
		||||
 | 
			
		||||
#### Software Updates and Installation:
 | 
			
		||||
Keep the server up-to-date with the latest security patches and software updates. The interface helps in installing and updating software packages and extensions.
 | 
			
		||||
 | 
			
		||||
#### Monitoring and Reporting:
 | 
			
		||||
Monitor server performance and health. The interface provides logs, status reports, and alerts for various server components, including disk usage, network traffic, and system load.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
### Installation and Access
 | 
			
		||||
 | 
			
		||||
Typically, smeserver-manager is installed by default on SME Server. To access the interface:
 | 
			
		||||
 | 
			
		||||
Open a web browser on a device connected to the same network as the SME Server. Enter the server's IP address or hostname followed by /server-manager in the address bar (e.g., https://192.168.1.1/server-manager). 
 | 
			
		||||
You will be prompted to log in. Use the administrator credentials set during the SME Server installation.
 | 
			
		||||
 | 
			
		||||
### Benefits
 | 
			
		||||
 | 
			
		||||
Simplicity: Makes it easy for non-technical users to manage a server.
 | 
			
		||||
Centralized Management: Provides a single point of control for various server functionalities.
 | 
			
		||||
Efficiency: Saves time and reduces complexity in server management tasks.
 | 
			
		||||
Security: Regular updates and built-in security features ensure the server remains secure.
 | 
			
		||||
 | 
			
		||||
### Considerations
 | 
			
		||||
 | 
			
		||||
Learning Curve: While designed to be user-friendly, some features may still require a basic understanding of server and network management concepts.
 | 
			
		||||
Dependencies: Relies on specific packages and configurations of SME Server, and may not be directly applicable to other Linux distributions.
 | 
			
		||||
 | 
			
		||||
### Summary
 | 
			
		||||
 | 
			
		||||
SMEserver-manager is a powerful tool that brings simplicity and efficiency to server management for small to medium-sized enterprises. By providing a centralized, web-based interface, it allows administrators to manage users, network settings, file sharing, email services, web hosting, and more, all from a single location.
 | 
			
		||||
@@ -33,6 +33,8 @@ use esmith::I18N;
 | 
			
		||||
 | 
			
		||||
use Data::Dumper;	# activate if DEBUG
 | 
			
		||||
 | 
			
		||||
binmode(STDOUT, ":encoding(UTF-8)");
 | 
			
		||||
 | 
			
		||||
my $navigation_ignore = 
 | 
			
		||||
	"(\.\.?|Swttheme\.pm|Login\.pm|Request\.pm|Modules\.pm(-.*)?)";
 | 
			
		||||
 | 
			
		||||
@@ -46,131 +48,193 @@ my @files = grep (!/^${navigation_ignore}$/, readdir (FUNCTIONS));
 | 
			
		||||
closedir FUNCTIONS;
 | 
			
		||||
 | 
			
		||||
my @langs = $i18n->availableLanguages(); 
 | 
			
		||||
#my @langs = ('en', 'fr'); 
 | 
			
		||||
#print Dumper(\@langs);
 | 
			
		||||
#my @langs = ('tr');  #Temp override
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
foreach my $lang (@langs)
 | 
			
		||||
{
 | 
			
		||||
    my $long_lex = SMNGR_LIB.'/'.I18NMODULES."/General/general_$lang.lex";
 | 
			
		||||
    next unless ( -e $long_lex );
 | 
			
		||||
	my $long_lex = SMNGR_LIB.'/'.I18NMODULES."/General/general_$lang.lex";
 | 
			
		||||
	next unless ( -e $long_lex );
 | 
			
		||||
 | 
			
		||||
    open(LEX, '<:encoding(UTF-8)', $long_lex)
 | 
			
		||||
         or die "Couldn't open ", $long_lex, " for reading.\n";
 | 
			
		||||
    my @gen_lex = <LEX>;
 | 
			
		||||
    close LEX;
 | 
			
		||||
	open(LEX, '<:encoding(UTF-8)', $long_lex)
 | 
			
		||||
		 or die "Couldn't open ", $long_lex, " for reading.\n";
 | 
			
		||||
	my @gen_lex = <LEX>;
 | 
			
		||||
	close LEX;
 | 
			
		||||
 | 
			
		||||
    foreach my $file (@files)
 | 
			
		||||
    {
 | 
			
		||||
	next if (-d SMNGR_LIB.'/'.WEBFUNCTIONS . "/$file");
 | 
			
		||||
#        next unless ( $file =~ m/D.*\.pm$/ );
 | 
			
		||||
        next unless ( $file =~ m/[A-Z].*\.pm$/ );
 | 
			
		||||
 | 
			
		||||
	my $file2 = lc($file);
 | 
			
		||||
	$file2 =~ s/\.pm$//;
 | 
			
		||||
 | 
			
		||||
    #-------------------------------------------------- 
 | 
			
		||||
    # extract heading, description and weight information
 | 
			
		||||
    # from Mojo controller
 | 
			
		||||
    #-------------------------------------------------- 
 | 
			
		||||
	open(SCRIPT, SMNGR_LIB.'/'.WEBFUNCTIONS . "/$file");
 | 
			
		||||
        my $heading            	= undef;
 | 
			
		||||
        my $description        	= undef;
 | 
			
		||||
        my $heading_weight     	= undef;
 | 
			
		||||
        my $description_weight 	= undef;
 | 
			
		||||
        my $menucat		= undef;
 | 
			
		||||
        my $routes		= undef;
 | 
			
		||||
 | 
			
		||||
        while ( <SCRIPT> )
 | 
			
		||||
	#my @files = ('Portforwarding.pm');  #Temp override
 | 
			
		||||
	foreach my $file (@files)
 | 
			
		||||
	{
 | 
			
		||||
	    $heading = $1 if (/^\s*#\s*heading\s*:\s*(.+?)\s*$/);
 | 
			
		||||
	    $description = $1 
 | 
			
		||||
		if (/^\s*#\s*description\s*:\s*(.+?)\s*$/);
 | 
			
		||||
	    ($heading_weight, $description_weight) = ($1, $2) 
 | 
			
		||||
		if (/^\s*#\s*navigation\s*:\s*(\d+?)\s+(\d+?)\s*$/);
 | 
			
		||||
	    $menucat = $1 
 | 
			
		||||
		if (/^\s*#\s*menu\s*:\s*(.+?)\s*$/);
 | 
			
		||||
		next if (-d SMNGR_LIB.'/'.WEBFUNCTIONS . "/$file");
 | 
			
		||||
		#        next unless ( $file =~ m/D.*\.pm$/ );
 | 
			
		||||
			next unless ( $file =~ m/[A-Z].*\.pm$/ );
 | 
			
		||||
 | 
			
		||||
	    last if (defined $heading and 
 | 
			
		||||
		defined $description and
 | 
			
		||||
		defined $heading_weight and
 | 
			
		||||
		defined $description_weight and
 | 
			
		||||
		defined $menucat);
 | 
			
		||||
		my $file2 = lc($file);
 | 
			
		||||
		$file2 =~ s/\.pm$//;
 | 
			
		||||
		#-------------------------------------------------- 
 | 
			
		||||
		# extract heading, description and weight information
 | 
			
		||||
		# from Mojo controller
 | 
			
		||||
		#-------------------------------------------------- 
 | 
			
		||||
		open(SCRIPT, SMNGR_LIB.'/'.WEBFUNCTIONS . "/$file");
 | 
			
		||||
		my $heading            	= undef;
 | 
			
		||||
		my $description        	= undef;
 | 
			
		||||
		my $heading_weight     	= undef;
 | 
			
		||||
		my $description_weight 	= undef;
 | 
			
		||||
		my $menucat		= undef;
 | 
			
		||||
		my $routes		= undef;
 | 
			
		||||
 | 
			
		||||
	    # routes : end  (stop before eof if 'menu' is not here before 'routes'!!!
 | 
			
		||||
	    $routes = $1 if (/^\s*#\s*routes\s*:\s*(.+?)\s*$/);
 | 
			
		||||
	    last if (defined $routes and $routes eq 'end');
 | 
			
		||||
		while ( <SCRIPT> )
 | 
			
		||||
		{
 | 
			
		||||
			$heading = $1 if (/^\s*#\s*heading\s*:\s*(.+?)\s*$/);
 | 
			
		||||
			$description = $1 
 | 
			
		||||
			if (/^\s*#\s*description\s*:\s*(.+?)\s*$/);
 | 
			
		||||
			($heading_weight, $description_weight) = ($1, $2) 
 | 
			
		||||
			if (/^\s*#\s*navigation\s*:\s*(\d+?)\s+(\d+?)\s*$/);
 | 
			
		||||
			$menucat = $1 
 | 
			
		||||
			if (/^\s*#\s*menu\s*:\s*(.+?)\s*$/);
 | 
			
		||||
 | 
			
		||||
			last if (defined $heading and 
 | 
			
		||||
			defined $description and
 | 
			
		||||
			defined $heading_weight and
 | 
			
		||||
			defined $description_weight and
 | 
			
		||||
			defined $menucat);
 | 
			
		||||
 | 
			
		||||
			# routes : end  (stop before eof if 'menu' is not here before 'routes'!!!
 | 
			
		||||
			$routes = $1 if (/^\s*#\s*routes\s*:\s*(.+?)\s*$/);
 | 
			
		||||
			last if (defined $routes and $routes eq 'end');
 | 
			
		||||
		}
 | 
			
		||||
		close SCRIPT;
 | 
			
		||||
 | 
			
		||||
		print "updating script $file for lang $lang\n"if DEBUG;
 | 
			
		||||
		my $navdb = $navdbs{$lang};
 | 
			
		||||
		my $navinfo = NAVDIR.'/'.NAVIGATIONDIR . "/navigation.$lang";
 | 
			
		||||
		$navdb ||= esmith::NavigationDB->open($navinfo);
 | 
			
		||||
		$navdb ||= esmith::NavigationDB->create($navinfo) or
 | 
			
		||||
			die "Couldn't create $navinfo\n";
 | 
			
		||||
			$navdbs{$lang} ||= $navdb;
 | 
			
		||||
		my $rec = $navdb->get($file2) || 
 | 
			
		||||
			$navdb->new_record($file2, { type => 'panel' } );
 | 
			
		||||
		
 | 
			
		||||
		my @panel_lex = ();
 | 
			
		||||
		$long_lex = SMNGR_LIB.'/'.I18NMODULES.'/'.ucfirst($file2)."/${file2}_$lang.lex";
 | 
			
		||||
		if ( -e $long_lex ) {
 | 
			
		||||
			open(LEX, '<:encoding(UTF-8)', $long_lex)
 | 
			
		||||
				 or die "Couldn't open ", $long_lex, " for reading.\n";
 | 
			
		||||
			@panel_lex = <LEX>;
 | 
			
		||||
			close LEX;
 | 
			
		||||
		}
 | 
			
		||||
		#Extract the prefix for this module
 | 
			
		||||
		my @keys = values @panel_lex;  # Get all values from the array
 | 
			
		||||
		
 | 
			
		||||
		my $i = 0;                    # Initialize the index
 | 
			
		||||
		my $found = 0;                # Flag to check if the prefix was found
 | 
			
		||||
		my $prefix = "xx_";			  # Probably never match!!
 | 
			
		||||
 | 
			
		||||
		while ($i < @keys) {          # Loop until we run out of entries
 | 
			
		||||
			my $extracted_value = $keys[$i] || "";  # The current entry
 | 
			
		||||
			#print("Extracted val: ".$extracted_value."\n");
 | 
			
		||||
 | 
			
		||||
			# Extract prefix from the second value (up to and including the first underscore)
 | 
			
		||||
			#my ($prefix) = $second_value =~ /^'(.*?_)/;  # Match everything up to and including the first underscore
 | 
			
		||||
			($prefix) = $extracted_value =~ /^'(.*?_)/;  # Match everything up to and including the first underscore
 | 
			
		||||
			
 | 
			
		||||
			if (defined $prefix) {
 | 
			
		||||
				$found = 1;           # Set found flag to true
 | 
			
		||||
				last;                 # Exit the loop if prefix is found
 | 
			
		||||
			} else {
 | 
			
		||||
				#print("Extracted Val: " . $extracted_value . "\n");
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			$i++;                     # Increment the index to check the next entry
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!$found) {
 | 
			
		||||
			print(STDERR "No valid prefix found in any entries: ".$file2." (".$lang.")\n"); # if DEBUG;
 | 
			
		||||
			$prefix = "xx_";			  # Probably never match!!
 | 
			
		||||
			
 | 
			
		||||
		}
 | 
			
		||||
		#print("Prefix: ".$prefix." ".$file2." (".$lang.")\n");
 | 
			
		||||
		
 | 
			
		||||
		my %Lexicon = ();
 | 
			
		||||
		push(@panel_lex, @gen_lex);
 | 
			
		||||
		my $top_error = 0;
 | 
			
		||||
 | 
			
		||||
		chomp @panel_lex;
 | 
			
		||||
		for (@panel_lex) {
 | 
			
		||||
			next unless $_;	# first one empty
 | 
			
		||||
			my ($k, $v) = split / => /, $_;
 | 
			
		||||
		#	errors on split to $v (use DEBUG to see)
 | 
			
		||||
			if ( $k and $v ) {
 | 
			
		||||
				$k =~ s/\'//g;
 | 
			
		||||
				$v =~ s/\'//g;
 | 
			
		||||
				$v =~ s/,$//g;
 | 
			
		||||
				$Lexicon{ lc($k) } = $v;
 | 
			
		||||
			} else {
 | 
			
		||||
				$k = "?" unless ($k);
 | 
			
		||||
				print STDERR "Error for $lang $file2 on $k \n" if DEBUG;
 | 
			
		||||
				$top_error++;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if ( $top_error > 0) {
 | 
			
		||||
			if ( DEBUG ) {
 | 
			
		||||
				print STDERR "$top_error errors for $lang $file2\n";
 | 
			
		||||
				#		print Dumper(\@panel_lex);
 | 
			
		||||
				#		print Dumper(\%Lexicon);
 | 
			
		||||
				#		exit 1;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		$heading = "" unless defined $heading;
 | 
			
		||||
		$description = "" unless defined $description;
 | 
			
		||||
		# Get the base language code from $lang
 | 
			
		||||
		my $base_lang = (split('-', $lang))[0];
 | 
			
		||||
		my $loc_heading =  process_localization( \%Lexicon, $heading, $lang, $prefix );
 | 
			
		||||
		my $loc_description =  process_localization( \%Lexicon, $description, $lang, $prefix );
 | 
			
		||||
		$loc_heading =~ s/^\s*(\w.*?)\s*$/$1/;
 | 
			
		||||
		$loc_description =~ s/^\s*(\w.*?)\s*$/$1/;
 | 
			
		||||
 | 
			
		||||
		$rec->merge_props(
 | 
			
		||||
			Heading => $loc_heading,
 | 
			
		||||
			Description => $loc_description,
 | 
			
		||||
			HeadingWeight => localise( \%Lexicon, $heading_weight ),
 | 
			
		||||
			DescriptionWeight => localise( \%Lexicon, $description_weight ),
 | 
			
		||||
			MenuCat => (defined $menucat ? $menucat : 'A'));
 | 
			
		||||
	}
 | 
			
		||||
	close SCRIPT;
 | 
			
		||||
 | 
			
		||||
	print "updating script $file for lang $lang\n" if DEBUG;
 | 
			
		||||
	#warn "trying to close for lang $lang\n";
 | 
			
		||||
	my $navdb = $navdbs{$lang};
 | 
			
		||||
	my $navinfo = NAVDIR.'/'.NAVIGATIONDIR . "/navigation.$lang";
 | 
			
		||||
	$navdb ||= esmith::NavigationDB->open($navinfo);
 | 
			
		||||
	$navdb ||= esmith::NavigationDB->create($navinfo) or
 | 
			
		||||
	    die "Couldn't create $navinfo\n";
 | 
			
		||||
        $navdbs{$lang} ||= $navdb;
 | 
			
		||||
	my $rec = $navdb->get($file2) || 
 | 
			
		||||
	    $navdb->new_record($file2, { type => 'panel' } );
 | 
			
		||||
 | 
			
		||||
	my @panel_lex = ();
 | 
			
		||||
	$long_lex = SMNGR_LIB.'/'.I18NMODULES.'/'.ucfirst($file2)."/${file2}_$lang.lex";
 | 
			
		||||
	if ( -e $long_lex ) {
 | 
			
		||||
	    open(LEX, '<:encoding(UTF-8)', $long_lex)
 | 
			
		||||
    		 or die "Couldn't open ", $long_lex, " for reading.\n";
 | 
			
		||||
	    @panel_lex = <LEX>;
 | 
			
		||||
	    close LEX;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	my %Lexicon = ();
 | 
			
		||||
	push(@panel_lex, @gen_lex);
 | 
			
		||||
	my $top_error = 0;
 | 
			
		||||
 | 
			
		||||
	chomp @panel_lex;
 | 
			
		||||
	for (@panel_lex) {
 | 
			
		||||
	    next unless $_;	# first one empty
 | 
			
		||||
	    my ($k, $v) = split / => /, $_;
 | 
			
		||||
#	errors on split to $v (use DEBUG to see)
 | 
			
		||||
	    if ( $k and $v ) {
 | 
			
		||||
		$k =~ s/\'//g;
 | 
			
		||||
		$v =~ s/\'//g;
 | 
			
		||||
		$v =~ s/,$//g;
 | 
			
		||||
		$Lexicon{ $k } = $v;
 | 
			
		||||
	    } else {
 | 
			
		||||
		$k = "?" unless ($k);
 | 
			
		||||
		print STDERR "Error for $lang $file2 on $k \n" if DEBUG;
 | 
			
		||||
		$top_error++;
 | 
			
		||||
	    }
 | 
			
		||||
	}
 | 
			
		||||
	if ( $top_error > 0) {
 | 
			
		||||
	    if ( DEBUG ) {
 | 
			
		||||
	        print STDERR "$top_error errors for $lang $file2\n";
 | 
			
		||||
#		print Dumper(\@panel_lex);
 | 
			
		||||
#		print Dumper(\%Lexicon);
 | 
			
		||||
#		exit 1;
 | 
			
		||||
	    }
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	my $loc_heading =  localise( \%Lexicon, $heading );
 | 
			
		||||
	my $loc_description =  localise( \%Lexicon, $description );
 | 
			
		||||
	$loc_heading =~ s/^\s*(\w.*?)\s*$/$1/;
 | 
			
		||||
	$loc_description =~ s/^\s*(\w.*?)\s*$/$1/;
 | 
			
		||||
 | 
			
		||||
	$rec->merge_props(
 | 
			
		||||
	    Heading => $loc_heading,
 | 
			
		||||
	    Description => $loc_description,
 | 
			
		||||
	    HeadingWeight => localise( \%Lexicon, $heading_weight ),
 | 
			
		||||
	    DescriptionWeight => localise( \%Lexicon, $description_weight ),
 | 
			
		||||
	    MenuCat => (defined $menucat ? $menucat : 'A'));
 | 
			
		||||
    }
 | 
			
		||||
    #warn "trying to close for lang $lang\n";
 | 
			
		||||
    my $navdb = $navdbs{$lang};
 | 
			
		||||
    $navdb->close();
 | 
			
		||||
	$navdb->close();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
sub localise {
 | 
			
		||||
    my ($lexicon, $string) = @_;
 | 
			
		||||
    #print("Looking up:".$string."\n");
 | 
			
		||||
    $string  = "" unless defined $string;
 | 
			
		||||
    return $lexicon->{$string} || $string;
 | 
			
		||||
    my $lc_string = lc($string);
 | 
			
		||||
    my $res = $lexicon->{$lc_string} || $string;
 | 
			
		||||
    #print("Returning:".$res."\n");
 | 
			
		||||
    return $res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Subroutine to process localization
 | 
			
		||||
sub process_localization {
 | 
			
		||||
    my ($lexicon_ref, $heading, $lang, $prefix) = @_;
 | 
			
		||||
 | 
			
		||||
    # Localized heading based on original heading
 | 
			
		||||
    my $loc_heading = localise($lexicon_ref, $heading);
 | 
			
		||||
 | 
			
		||||
    # Get the base language code from $lang
 | 
			
		||||
    my $base_lang = (split('-', $lang))[0];
 | 
			
		||||
 | 
			
		||||
    # Check the condition
 | 
			
		||||
    if ($loc_heading eq $heading && $base_lang ne 'en') {
 | 
			
		||||
        # Construct the new key by combining the prefix and the original heading
 | 
			
		||||
        my $key = $prefix . $heading;
 | 
			
		||||
        # Localize using the constructed key
 | 
			
		||||
        $loc_heading = localise($lexicon_ref, $key);
 | 
			
		||||
        # See if it got a hit
 | 
			
		||||
        if ($loc_heading eq $key){
 | 
			
		||||
			$loc_heading = $heading;
 | 
			
		||||
		}
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    return $loc_heading; # Optionally return the localized heading
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -67,5 +67,21 @@ hr.sme-copyrightbar {
 | 
			
		||||
color: #8ebe43;
 | 
			
		||||
background-color: #8ebe43;
 | 
			
		||||
    }
 | 
			
		||||
      
 | 
			
		||||
/* flag container no flag */
 | 
			
		||||
#flag-container span {
 | 
			
		||||
  font-size: 24px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.fallback-box {
 | 
			
		||||
  display: inline-block; /* Make it inline-block to fit around the content */
 | 
			
		||||
  border: 2px solid gray; /* Change the border color as desired */
 | 
			
		||||
  padding: 10px; /* Add some padding */
 | 
			
		||||
  border-radius: 10px; /* Round the corners of the box */
 | 
			
		||||
  font-size: 60px; /* Adjust size if needed */
 | 
			
		||||
  margin-top: 10px; /* Add some margin */
 | 
			
		||||
  text-align: center; /* Center text inside the box */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HERE
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -234,7 +234,7 @@ a.menu-title {
 | 
			
		||||
    width: 100%;
 | 
			
		||||
}
 | 
			
		||||
#h2e11 {
 | 
			
		||||
    width: 70%;
 | 
			
		||||
    width: 50%;
 | 
			
		||||
    float: left;
 | 
			
		||||
}
 | 
			
		||||
#h2e12 {
 | 
			
		||||
@@ -257,9 +257,9 @@ a.menu-title {
 | 
			
		||||
    width: 70%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#h2e22,#h2e23 {
 | 
			
		||||
#h2e22,#h2e23,#h2e12 {
 | 
			
		||||
    float: left;
 | 
			
		||||
    width: 10%;
 | 
			
		||||
    width: 14em;
 | 
			
		||||
    text-align: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -273,6 +273,7 @@ a.menu-title {
 | 
			
		||||
    left: 2px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*end*/
 | 
			
		||||
EOF
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,27 @@
 | 
			
		||||
document.addEventListener('DOMContentLoaded', () => {
 | 
			
		||||
  const flagContainer = document.getElementById('flag-container');
 | 
			
		||||
 | 
			
		||||
  // Function to get the browser's locale
 | 
			
		||||
  function getBrowserLocale() {
 | 
			
		||||
    return navigator.language || navigator.userLanguage;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Function to map locale to country code
 | 
			
		||||
  function getCountryCodeFromLocale(locale) {
 | 
			
		||||
    const localeParts = locale.split('-');
 | 
			
		||||
    return localeParts.length > 1 ? localeParts[1] : localeParts[0];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Function to create and display the flag icon
 | 
			
		||||
  function displayFlagIcon(countryCode) {
 | 
			
		||||
    const flagIcon = document.createElement('span');
 | 
			
		||||
    flagIcon.className = `flag-icon flag-icon-${countryCode.toLowerCase()}`;
 | 
			
		||||
    flagIcon.id = 'flag-icon';
 | 
			
		||||
    flagContainer.appendChild(flagIcon);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // Main logic
 | 
			
		||||
  const locale = getBrowserLocale();
 | 
			
		||||
  const countryCode = getCountryCodeFromLocale(locale);
 | 
			
		||||
  displayFlagIcon(countryCode);
 | 
			
		||||
});
 | 
			
		||||
@@ -23,14 +23,16 @@ use File::Basename;
 | 
			
		||||
 | 
			
		||||
our $cdb = esmith::ConfigDB->open || die "Couldn't open config db";
 | 
			
		||||
 | 
			
		||||
my $dnf_status_file  = '/var/cache/dnf/dnf.status';
 | 
			
		||||
 | 
			
		||||
#use File::stat;
 | 
			
		||||
 | 
			
		||||
our %dbs;
 | 
			
		||||
 | 
			
		||||
for ( qw(available installed updates) )
 | 
			
		||||
{
 | 
			
		||||
    $dbs{$_} = esmith::ConfigDB->open_ro("yum_$_") or
 | 
			
		||||
	die "Couldn't open yum_$_ DB\n";
 | 
			
		||||
    $dbs{$_} = esmith::ConfigDB->open_ro("dnf_$_") or
 | 
			
		||||
	die "Couldn't open dnf_$_ DB\n";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
for ( qw(repositories) )
 | 
			
		||||
@@ -52,10 +54,11 @@ sub main {
 | 
			
		||||
 | 
			
		||||
    $yum_datas{'trt'} = 'STAT';
 | 
			
		||||
 | 
			
		||||
    if ( -e "/var/run/yum.pid" ) {
 | 
			
		||||
   
 | 
			
		||||
    if ( $c->is_dnf_running()) {
 | 
			
		||||
	$yum_datas{'trt'} = 'LOGF';
 | 
			
		||||
	$dest = 'yumlogfile';
 | 
			
		||||
    } elsif ($cdb->get_prop('yum', 'LogFile')) {
 | 
			
		||||
    } elsif ($cdb->get_prop('dnf', 'LogFile')) {
 | 
			
		||||
	$yum_datas{'trt'} = 'PSTU';
 | 
			
		||||
	$yum_datas{'reconf'} = $cdb->get_value('UnsavedChanges', 'yes');
 | 
			
		||||
	$dest = 'yumpostupg';
 | 
			
		||||
@@ -83,9 +86,9 @@ sub do_display {
 | 
			
		||||
    $yum_datas{'trt'} = $trt;
 | 
			
		||||
 | 
			
		||||
    # force $trt if current logfile
 | 
			
		||||
    if ( -e "/var/run/yum.pid" ) {
 | 
			
		||||
    if ( $c->is_dnf_running() ) {
 | 
			
		||||
	$trt = 'LOGF';
 | 
			
		||||
    } elsif ($cdb->get_prop('yum', 'LogFile')) {
 | 
			
		||||
    } elsif ($cdb->get_prop('dnf', 'LogFile')) {
 | 
			
		||||
	$trt = 'PSTU';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -106,13 +109,13 @@ sub do_display {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
        if ( $trt eq 'LOGF' ) {
 | 
			
		||||
	    if (-e "/var/run/yum.pid") {
 | 
			
		||||
	    if ($c->is_dnf_running()) {
 | 
			
		||||
	    $dest = 'yumlogfile';
 | 
			
		||||
	    }
 | 
			
		||||
	}
 | 
			
		||||
	
 | 
			
		||||
        if ( $trt eq 'PSTU') {
 | 
			
		||||
	    if ($cdb->get_prop('yum', 'LogFile')) {
 | 
			
		||||
	    if ($cdb->get_prop('dnf', 'LogFile')) {
 | 
			
		||||
		$dest = 'yumpostupg';
 | 
			
		||||
		$yum_datas{'reconf'} = $cdb->get_value('UnsavedChanges', 'yes');
 | 
			
		||||
	    }
 | 
			
		||||
@@ -221,7 +224,7 @@ sub do_update {
 | 
			
		||||
    if ( $trt eq 'LOGF' ) {
 | 
			
		||||
 | 
			
		||||
    	$dest = 'yumlogfile';
 | 
			
		||||
        if ( ! -e "/var/run/yum.pid") {
 | 
			
		||||
        if ( ! $c->is_dnf_running()) {
 | 
			
		||||
	    $yum_datas{trt} = 'SUC';
 | 
			
		||||
	    $result = $c->l('yum_SUCCESS');
 | 
			
		||||
        }
 | 
			
		||||
@@ -247,6 +250,24 @@ sub do_update {
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
sub get_dnf_status {
 | 
			
		||||
    #interrogate status file created and maintained by smeserver.py plugin for dnf.
 | 
			
		||||
    my ($c) = @_;
 | 
			
		||||
    my $file_name = $dnf_status_file;
 | 
			
		||||
    my $content = "resolved";
 | 
			
		||||
    if ( -e "$file_name") {
 | 
			
		||||
	open my $fh, '<', $file_name or die "Can't open file: $!";
 | 
			
		||||
	$content = <$fh>;
 | 
			
		||||
	close $fh;
 | 
			
		||||
    }
 | 
			
		||||
    return $content;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub is_dnf_running {
 | 
			
		||||
    my ($c) = @_;
 | 
			
		||||
    my $dnf_status = $c->get_dnf_status();
 | 
			
		||||
    return $dnf_status ne "resolved" && $dnf_status ne "config" && $dnf_status ne "sack";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub is_empty {
 | 
			
		||||
 | 
			
		||||
@@ -289,7 +310,7 @@ sub package_functions_enabled {
 | 
			
		||||
 | 
			
		||||
    my ($c) = @_;
 | 
			
		||||
 | 
			
		||||
    return ($cdb->get_prop("yum", "PackageFunctions") eq "enabled");
 | 
			
		||||
    return ($cdb->get_prop("dnf", "PackageFunctions") eq "enabled");
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -298,7 +319,7 @@ sub get_status {
 | 
			
		||||
 | 
			
		||||
    my ($c, $prop, $localise) = @_;
 | 
			
		||||
 | 
			
		||||
    my $status = $cdb->get_prop("yum", $prop) || 'disabled';
 | 
			
		||||
    my $status = $cdb->get_prop("dnf", $prop) || 'disabled';
 | 
			
		||||
 | 
			
		||||
    return $status unless $localise;
 | 
			
		||||
 | 
			
		||||
@@ -433,7 +454,7 @@ sub change_settings {
 | 
			
		||||
			PackageFunctions
 | 
			
		||||
            	) )
 | 
			
		||||
    {
 | 
			
		||||
	$cdb->set_prop('yum', $param, $c->param("yum_$param"));
 | 
			
		||||
	$cdb->set_prop("dnf", $param, $c->param("yum_$param"));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    my $check4updates = $c->param("yum_check4updates");
 | 
			
		||||
@@ -441,21 +462,21 @@ sub change_settings {
 | 
			
		||||
 | 
			
		||||
    if ($check4updates ne 'disabled') { $status = 'enabled'; }
 | 
			
		||||
 | 
			
		||||
    $cdb->set_prop('yum', 'check4updates', $check4updates);
 | 
			
		||||
    $cdb->set_prop("dnf", 'check4updates', $check4updates);
 | 
			
		||||
 | 
			
		||||
    my $deltarpm = $c->param("yum_DeltaRpmProcess");
 | 
			
		||||
    $cdb->set_prop('yum', 'DeltaRpmProcess', $deltarpm);
 | 
			
		||||
    $cdb->set_prop("dnf", 'DeltaRpmProcess', $deltarpm);
 | 
			
		||||
 | 
			
		||||
    my $downloadonly = $c->param("yum_DownloadOnly");
 | 
			
		||||
    if ($downloadonly ne 'disabled') { $status = 'enabled'; }
 | 
			
		||||
 | 
			
		||||
    $cdb->set_prop('yum', 'DownloadOnly', $downloadonly);
 | 
			
		||||
    $cdb->set_prop("dnf", 'DownloadOnly', $downloadonly);
 | 
			
		||||
 | 
			
		||||
    my $AutoInstallUpdates = $c->param("yum_AutoInstallUpdates");
 | 
			
		||||
    if ($AutoInstallUpdates ne 'disabled') { $status = 'enabled'; }
 | 
			
		||||
 | 
			
		||||
    $cdb->set_prop('yum', 'AutoInstallUpdates', $AutoInstallUpdates);
 | 
			
		||||
    $cdb->set_prop('yum', 'status', $status);
 | 
			
		||||
    $cdb->set_prop("dnf", 'AutoInstallUpdates', $AutoInstallUpdates);
 | 
			
		||||
    $cdb->set_prop("dnf", 'status', $status);
 | 
			
		||||
 | 
			
		||||
    my %selected = map {$_ => 1} @{$c->every_param('SelectedRepositories')};
 | 
			
		||||
 | 
			
		||||
@@ -469,7 +490,7 @@ sub change_settings {
 | 
			
		||||
 | 
			
		||||
    $dbs{repositories}->reload;
 | 
			
		||||
 | 
			
		||||
    unless ( system( "/sbin/e-smith/signal-event", "yum-modify" ) == 0 )
 | 
			
		||||
    unless ( system( "/sbin/e-smith/signal-event", "dnf-modify" ) == 0 )
 | 
			
		||||
    {
 | 
			
		||||
	return $c->l('yum_ERROR_UPDATING_CONFIGURATION');
 | 
			
		||||
    }
 | 
			
		||||
@@ -484,11 +505,11 @@ sub do_yum {
 | 
			
		||||
 | 
			
		||||
    for ( qw(SelectedGroups SelectedPackages) )
 | 
			
		||||
    {
 | 
			
		||||
	$cdb->set_prop("yum", $_, join(',', (@{$c->every_param($_)} )));
 | 
			
		||||
	$cdb->set_prop("dnf", $_, join(',', (@{$c->every_param($_)} )));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    esmith::util::backgroundCommand(0,
 | 
			
		||||
        "/sbin/e-smith/signal-event", "yum-$function");
 | 
			
		||||
        "/sbin/e-smith/signal-event", "dnf-$function");
 | 
			
		||||
 | 
			
		||||
    for ( qw(available installed updates) ) {
 | 
			
		||||
	$dbs{$_}->reload;
 | 
			
		||||
@@ -517,7 +538,7 @@ sub format_yum_log {
 | 
			
		||||
 | 
			
		||||
    $cdb->reload;
 | 
			
		||||
 | 
			
		||||
    my $filepage = $cdb->get_prop('yum', 'LogFile');
 | 
			
		||||
    my $filepage = $cdb->get_prop('dnf', 'LogFile');
 | 
			
		||||
    return '' unless $filepage and ( -e "$filepage" );
 | 
			
		||||
 | 
			
		||||
    my $out = sprintf "<PRE>";
 | 
			
		||||
@@ -537,7 +558,7 @@ sub post_upgrade_reboot {
 | 
			
		||||
 | 
			
		||||
    my $c = shift;
 | 
			
		||||
 | 
			
		||||
    $cdb->get_prop_and_delete('yum', 'LogFile');
 | 
			
		||||
    $cdb->get_prop_and_delete('dnf', 'LogFile');
 | 
			
		||||
    $cdb->reload;
 | 
			
		||||
 | 
			
		||||
    if (fork == 0) {
 | 
			
		||||
@@ -552,7 +573,7 @@ sub post_upgrade_reboot {
 | 
			
		||||
sub show_yum_log {
 | 
			
		||||
    my $c = shift;
 | 
			
		||||
    my $out = $c->format_yum_log();
 | 
			
		||||
    my $yum_log = $cdb->get_prop_and_delete('yum', 'LogFile');
 | 
			
		||||
    my $yum_log = $cdb->get_prop_and_delete('dnf', 'LogFile');
 | 
			
		||||
    return $out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,278 @@
 | 
			
		||||
document.addEventListener('DOMContentLoaded', () => {
 | 
			
		||||
  const flagContainer = document.getElementById('flag-container');
 | 
			
		||||
 | 
			
		||||
        async function getCountryName(countryCode) {
 | 
			
		||||
            try {
 | 
			
		||||
                const response = await fetch(`https://restcountries.com/v3.1/alpha/${countryCode}`);
 | 
			
		||||
                if (!response.ok) throw new Error('Country not found');
 | 
			
		||||
                const data = await response.json();
 | 
			
		||||
                // Return the name in the native language
 | 
			
		||||
                return data[0].name.common; 
 | 
			
		||||
            } catch (error) {
 | 
			
		||||
                console.error(error);
 | 
			
		||||
                return 'Unknown Country';
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function getFlagEmoji(locale) {
 | 
			
		||||
            // Split the locale to get the language and country code
 | 
			
		||||
            const parts = locale.split('-');
 | 
			
		||||
            let countryCode;
 | 
			
		||||
 | 
			
		||||
            // Handle single subtag (language only) or double subtag (language-country)
 | 
			
		||||
            if (parts.length === 1) {
 | 
			
		||||
                countryCode = getCountryCodeFromLanguage(parts[0]);
 | 
			
		||||
            } else if (parts.length === 2) {
 | 
			
		||||
                countryCode = parts[1].toLowerCase(); // Use the country code
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // If country code is not found, set a fallback output
 | 
			
		||||
            if (!countryCode) {
 | 
			
		||||
                const fallback = `? ${locale.toUpperCase()}`; // Just a question mark and the full locale
 | 
			
		||||
                return { flag: fallback, isUnknown: true, countryName: 'Unknown Country' };
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Convert the country code to a flag emoji
 | 
			
		||||
            return {
 | 
			
		||||
                flag: String.fromCodePoint(...[...countryCode.toUpperCase()].map(char => 0x1F1E6 + char.charCodeAt(0) - 'A'.charCodeAt(0))),
 | 
			
		||||
                isUnknown: false,
 | 
			
		||||
                countryCode: countryCode
 | 
			
		||||
            };
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        function getCountryCodeFromLanguage(language) {
 | 
			
		||||
            // Map languages to countries (this is an example, extend as needed)
 | 
			
		||||
            const languageToCountryMap = {
 | 
			
		||||
                  // Add more mappings as needed
 | 
			
		||||
    "af": "NA",
 | 
			
		||||
    "agq": "CM",
 | 
			
		||||
    "ak": "GH",
 | 
			
		||||
    "am": "ET",
 | 
			
		||||
    "ar": "01",
 | 
			
		||||
    "as": "IN",
 | 
			
		||||
    "asa": "TZ",
 | 
			
		||||
    "ast": "ES",
 | 
			
		||||
    "az": "rl",
 | 
			
		||||
    "bas": "CM",
 | 
			
		||||
    "be": "BY",
 | 
			
		||||
    "bem": "ZM",
 | 
			
		||||
    "bez": "TZ",
 | 
			
		||||
    "bg": "BG",
 | 
			
		||||
    "bm": "ML",
 | 
			
		||||
    "bn": "BD",
 | 
			
		||||
    "bo": "CN",
 | 
			
		||||
    "br": "FR",
 | 
			
		||||
    "brx": "IN",
 | 
			
		||||
    "bs": "rl",
 | 
			
		||||
    "ca": "AD",
 | 
			
		||||
    "ccp": "BD",
 | 
			
		||||
    "ce": "RU",
 | 
			
		||||
    "cgg": "UG",
 | 
			
		||||
    "chr": "US",
 | 
			
		||||
    "ckb": "IQ",
 | 
			
		||||
    "cs": "CZ",
 | 
			
		||||
    "cy": "GB",
 | 
			
		||||
    "da": "DK",
 | 
			
		||||
    "dav": "KE",
 | 
			
		||||
    "de": "DE",
 | 
			
		||||
    "dje": "NE",
 | 
			
		||||
    "dsb": "DE",
 | 
			
		||||
    "dua": "CM",
 | 
			
		||||
    "dyo": "SN",
 | 
			
		||||
    "dz": "BT",
 | 
			
		||||
    "ebu": "KE",
 | 
			
		||||
    "ee": "GH",
 | 
			
		||||
    "el": "CY",
 | 
			
		||||
    "en": "01",
 | 
			
		||||
    "es": "ES",
 | 
			
		||||
    "et": "EE",
 | 
			
		||||
    "eu": "ES",
 | 
			
		||||
    "ewo": "CM",
 | 
			
		||||
    "fa": "AF",
 | 
			
		||||
    "ff": "CM",
 | 
			
		||||
    "fi": "FI",
 | 
			
		||||
    "fil": "PH",
 | 
			
		||||
    "fo": "FO",
 | 
			
		||||
    "fr": "FR",
 | 
			
		||||
    "fur": "IT",
 | 
			
		||||
    "fy": "NL",
 | 
			
		||||
    "ga": "IE",
 | 
			
		||||
    "gd": "GB",
 | 
			
		||||
    "gl": "ES",
 | 
			
		||||
    "gsw": "CH",
 | 
			
		||||
    "gu": "IN",
 | 
			
		||||
    "guz": "KE",
 | 
			
		||||
    "gv": "IM",
 | 
			
		||||
    "ha": "GH",
 | 
			
		||||
    "haw": "US",
 | 
			
		||||
    "he": "IL",
 | 
			
		||||
    "hi": "IN",
 | 
			
		||||
    "hr": "HR",
 | 
			
		||||
    "hsb": "DE",
 | 
			
		||||
    "hu": "HU",
 | 
			
		||||
    "hy": "AM",
 | 
			
		||||
    "id": "ID",
 | 
			
		||||
    "ig": "NG",
 | 
			
		||||
    "ii": "CN",
 | 
			
		||||
    "is": "IS",
 | 
			
		||||
    "it": "IT",
 | 
			
		||||
    "ja": "JP",
 | 
			
		||||
    "jgo": "CM",
 | 
			
		||||
    "jmc": "TZ",
 | 
			
		||||
    "ka": "GE",
 | 
			
		||||
    "kab": "DZ",
 | 
			
		||||
    "kam": "KE",
 | 
			
		||||
    "kde": "TZ",
 | 
			
		||||
    "kea": "CV",
 | 
			
		||||
    "khq": "ML",
 | 
			
		||||
    "ki": "KE",
 | 
			
		||||
    "kk": "KZ",
 | 
			
		||||
    "kkj": "CM",
 | 
			
		||||
    "kl": "GL",
 | 
			
		||||
    "kln": "KE",
 | 
			
		||||
    "km": "KH",
 | 
			
		||||
    "kn": "IN",
 | 
			
		||||
    "ko": "KP",
 | 
			
		||||
    "kok": "IN",
 | 
			
		||||
    "ks": "IN",
 | 
			
		||||
    "ksb": "TZ",
 | 
			
		||||
    "ksf": "CM",
 | 
			
		||||
    "ksh": "DE",
 | 
			
		||||
    "kw": "GB",
 | 
			
		||||
    "ky": "KG",
 | 
			
		||||
    "lag": "TZ",
 | 
			
		||||
    "lb": "LU",
 | 
			
		||||
    "lg": "UG",
 | 
			
		||||
    "lkt": "US",
 | 
			
		||||
    "ln": "AO",
 | 
			
		||||
    "lo": "LA",
 | 
			
		||||
    "lrc": "IQ",
 | 
			
		||||
    "lt": "LT",
 | 
			
		||||
    "lu": "CD",
 | 
			
		||||
    "luo": "KE",
 | 
			
		||||
    "Luo": "KE",
 | 
			
		||||
    "luy": "KE",
 | 
			
		||||
    "lv": "LV",
 | 
			
		||||
    "mas": "KE",
 | 
			
		||||
    "mer": "KE",
 | 
			
		||||
    "mfe": "MU",
 | 
			
		||||
    "mg": "MG",
 | 
			
		||||
    "mgh": "MZ",
 | 
			
		||||
    "mgo": "CM",
 | 
			
		||||
    "mk": "MK",
 | 
			
		||||
    "ml": "IN",
 | 
			
		||||
    "mn": "MN",
 | 
			
		||||
    "mr": "IN",
 | 
			
		||||
    "ms": "BN",
 | 
			
		||||
    "mt": "MT",
 | 
			
		||||
    "mua": "CM",
 | 
			
		||||
    "my": "MM",
 | 
			
		||||
    "mzn": "IR",
 | 
			
		||||
    "naq": "NA",
 | 
			
		||||
    "nb": "NO",
 | 
			
		||||
    "nd": "ZW",
 | 
			
		||||
    "nds": "DE",
 | 
			
		||||
    "ne": "IN",
 | 
			
		||||
    "nl": "NL",
 | 
			
		||||
    "nmg": "CM",
 | 
			
		||||
    "nn": "NO",
 | 
			
		||||
    "nnh": "CM",
 | 
			
		||||
    "nus": "SS",
 | 
			
		||||
    "nyn": "UG",
 | 
			
		||||
    "om": "ET",
 | 
			
		||||
    "or": "IN",
 | 
			
		||||
    "os": "GE",
 | 
			
		||||
    "pa": "ab",
 | 
			
		||||
    "pl": "PL",
 | 
			
		||||
    "ps": "AF",
 | 
			
		||||
    "pt": "PT",
 | 
			
		||||
    "qu": "BO",
 | 
			
		||||
    "rm": "CH",
 | 
			
		||||
    "rn": "BI",
 | 
			
		||||
    "ro": "RO",
 | 
			
		||||
    "rof": "TZ",
 | 
			
		||||
    "ru": "RU",
 | 
			
		||||
    "rw": "RW",
 | 
			
		||||
    "rwk": "TZ",
 | 
			
		||||
    "sah": "RU",
 | 
			
		||||
    "saq": "KE",
 | 
			
		||||
    "sbp": "TZ",
 | 
			
		||||
    "se": "SE",
 | 
			
		||||
    "seh": "MZ",
 | 
			
		||||
    "ses": "ML",
 | 
			
		||||
    "sg": "CF",
 | 
			
		||||
    "shi": "tn",
 | 
			
		||||
    "si": "LK",
 | 
			
		||||
    "sk": "SK",
 | 
			
		||||
    "sl": "SI",
 | 
			
		||||
    "smn": "FI",
 | 
			
		||||
    "sn": "ZW",
 | 
			
		||||
    "so": "SO",
 | 
			
		||||
    "sq": "AL",
 | 
			
		||||
    "sr": "rl",
 | 
			
		||||
    "sv": "AX",
 | 
			
		||||
    "sw": "CD",
 | 
			
		||||
    "ta": "IN",
 | 
			
		||||
    "te": "IN",
 | 
			
		||||
    "teo": "KE",
 | 
			
		||||
    "tg": "TJ",
 | 
			
		||||
    "th": "TH",
 | 
			
		||||
    "ti": "ER",
 | 
			
		||||
    "to": "TO",
 | 
			
		||||
    "tr": "TR",
 | 
			
		||||
    "tt": "RU",
 | 
			
		||||
    "twq": "NE",
 | 
			
		||||
    "tzm": "MA",
 | 
			
		||||
    "ug": "CN",
 | 
			
		||||
    "uk": "UA",
 | 
			
		||||
    "ur": "IN",
 | 
			
		||||
    "uz": "ab",
 | 
			
		||||
    "vai": "tn",
 | 
			
		||||
    "Vai": "tn",
 | 
			
		||||
    "vi": "VN",
 | 
			
		||||
    "vun": "TZ",
 | 
			
		||||
    "wae": "CH",
 | 
			
		||||
    "wo": "SN",
 | 
			
		||||
    "xog": "UG",
 | 
			
		||||
    "yav": "CM",
 | 
			
		||||
    "yi": "01",
 | 
			
		||||
    "yo": "BJ",
 | 
			
		||||
    "yue": "ns",
 | 
			
		||||
    "zgh": "MA",
 | 
			
		||||
    "zh": "ns",
 | 
			
		||||
    "zu": "ZA"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            return languageToCountryMap[language] || null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        async function displayLocaleAndFlag() {
 | 
			
		||||
            // Get the browser locale
 | 
			
		||||
            const userLocale = navigator.language || navigator.userLanguage;
 | 
			
		||||
            const { flag, isUnknown, countryCode } = getFlagEmoji(userLocale);
 | 
			
		||||
 | 
			
		||||
            // Display the locale and the corresponding flag (or fallback)
 | 
			
		||||
            //document.getElementById('locale').textContent = `Your Locale: ${userLocale}`;
 | 
			
		||||
 | 
			
		||||
            if (isUnknown) {
 | 
			
		||||
                const fallbackDiv = document.createElement('div');
 | 
			
		||||
                fallbackDiv.className = 'fallback-box';
 | 
			
		||||
                fallbackDiv.textContent = `? ${userLocale.toUpperCase()}`; // Only show ? and locale code inside the box
 | 
			
		||||
                //document.getElementById('flag-container').textContent = "Flag: ";
 | 
			
		||||
                document.getElementById('flag-container').appendChild(fallbackDiv);
 | 
			
		||||
                // Tooltip for fallback
 | 
			
		||||
                fallbackDiv.title = "Unknown Country"; // Tooltip for fallback
 | 
			
		||||
            } else {
 | 
			
		||||
                const countryName = await getCountryName(countryCode);
 | 
			
		||||
                const flagSpan = document.createElement('span');
 | 
			
		||||
                flagSpan.textContent = flag; // Use flag emoji
 | 
			
		||||
                flagSpan.title = countryName; // Tooltip for the flag in country language
 | 
			
		||||
                //document.getElementById('flag-container').textContent = "Flag: ";
 | 
			
		||||
                document.getElementById('flag-container').appendChild(flagSpan);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        displayLocaleAndFlag();
 | 
			
		||||
});
 | 
			
		||||
@@ -31,7 +31,9 @@
 | 
			
		||||
	%= javascript '/js/vfs_fonts.js'
 | 
			
		||||
	%= javascript '/js/buttons.html5.min.js'
 | 
			
		||||
	%= javascript '/js/buttons.print.min.js'
 | 
			
		||||
	%= javascript '/js/flag-by-locale.js'
 | 
			
		||||
 | 
			
		||||
	<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.5.0/css/flag-icon.min.css">
 | 
			
		||||
	%= stylesheet '/css/sme-jquery-overrides.css'
 | 
			
		||||
 | 
			
		||||
</head>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,27 +1,128 @@
 | 
			
		||||
<style>
 | 
			
		||||
#h2l1 {
 | 
			
		||||
    display: flex;                  /* Use flexbox layout */
 | 
			
		||||
    justify-content: space-between; /* Space between child elements */
 | 
			
		||||
    align-items: center;           /* Center items vertically */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#h2e11 {
 | 
			
		||||
    flex: 1;                       /* Allow the first div to take available space on the left */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#h2e12 { 
 | 
			
		||||
    display: flex;                 /* Make this div a flex container */
 | 
			
		||||
    align-items: center;           /* Vertically center content in this div */
 | 
			
		||||
    margin-left: 20px;            /* Add margin to the left of this div */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#flag-container {
 | 
			
		||||
    display: flex;                 /* Make this div a flex container */
 | 
			
		||||
    align-items: center;           /* Vertically center content in this div */
 | 
			
		||||
    margin-left: 20px;             /* Add margin to the left of the flag container */
 | 
			
		||||
    padding-right: 20px;           /* Add padding to the right side of the flag container */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.flag-icon {
 | 
			
		||||
    width: 43px;
 | 
			
		||||
    font-size: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#legacy-button button {
 | 
			
		||||
    background-color: #98d36e; /* Button background color */
 | 
			
		||||
    font-weight: lighter;      /* Lighter text for the button */
 | 
			
		||||
    color: #063;               /* Button text color */
 | 
			
		||||
    padding: 5px 10px;         /* Padding for the button */
 | 
			
		||||
    border: none;              /* Remove default button border */
 | 
			
		||||
    border-radius: 4px;        /* Rounded corners for button */
 | 
			
		||||
    cursor: pointer;           /* Change cursor on hover */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#legacy-button button:hover {
 | 
			
		||||
    background-color: #82c961; /* Change color on hover */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.infobar {
 | 
			
		||||
    display: flex;                  /* Use flexbox for alignment */
 | 
			
		||||
    justify-content: space-between; /* Space between child elements */
 | 
			
		||||
    align-items: center;            /* Center items vertically */
 | 
			
		||||
    padding: 10px;                  /* Adjust padding as desired */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#h2e21 {
 | 
			
		||||
    flex: 1;                        /* Allow the first div to take available space on the left */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#h2e22 {
 | 
			
		||||
    display: flex;                  /* Make this div a flex container */
 | 
			
		||||
    justify-content: center;        /* Center the help button */
 | 
			
		||||
    align-items: center;            /* Center vertically */
 | 
			
		||||
    flex: none;                    /* Prevent this div from growing */
 | 
			
		||||
    position: relative;             /* Relative position for centering */
 | 
			
		||||
    width: 100px;                  /* Set a width for the help button container */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#h2e23 {
 | 
			
		||||
    margin-left: auto;              /* Push the third div to the right */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-button {
 | 
			
		||||
    background-color: #98d36e;     /* Button background color */
 | 
			
		||||
    font-weight: bold;              /* Bold text */
 | 
			
		||||
    xxcolor: #ffffff;                 /* Button text color */
 | 
			
		||||
    padding: 8px 12px;              /* Adjust padding for button height */
 | 
			
		||||
    border: none;                   /* Remove default border */
 | 
			
		||||
    border-radius: 4px;             /* Rounded corners */
 | 
			
		||||
    cursor: pointer;                /* Change cursor on hover */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-button:hover {
 | 
			
		||||
    background-color: #82c961;      /* Change color on hover */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#help-button {
 | 
			
		||||
    text-decoration: none;          /* Remove underline from link */
 | 
			
		||||
    font-size: 20px;                /* Adjust font size as needed */
 | 
			
		||||
    padding: 8px 12px;              /* Add padding to the help link */
 | 
			
		||||
    background-color: #98d36e;      /* Background for visibility */
 | 
			
		||||
    border-radius: 4px;             /* Rounded corners */
 | 
			
		||||
    color: #000;                    /* Button text color */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#help-button:hover {
 | 
			
		||||
    background-color: #82c961;      /* Change background on hover */
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
 | 
			
		||||
<div id="header2" class="hd2">
 | 
			
		||||
    <div id="h2l1"> 
 | 
			
		||||
	<div id="h2e11">
 | 
			
		||||
	    <a target='_blank' href="http://www.koozali.org"><img src="images/smeserver_logo.jpg" height="40" alt="SME Server"></a>
 | 
			
		||||
	</div>
 | 
			
		||||
<!--        <div id="h2e12"><h5><a href="initial">Server Manager</a>
 | 
			
		||||
            <a href="/server-manager" target='_blank'>    (Prev SM)</a></h5> -->           
 | 
			
		||||
        <div id="h2e12" style="float:right;"><br><a href="/server-manager" target='_blank'><button style="background-color:#98d36e;font-weight:bold;color:#063;">Legacy SM</button></a></div>
 | 
			
		||||
    </div>
 | 
			
		||||
		<div id="h2e11">
 | 
			
		||||
			<a target='_blank' href="http://www.koozali.org"><img src="images/smeserver_logo.jpg" height="40" alt="SME Server"></a>
 | 
			
		||||
		</div> 
 | 
			
		||||
 | 
			
		||||
    <div id="h2l2" class="infobar"> 
 | 
			
		||||
	<div id="h2e21">
 | 
			
		||||
        <b>
 | 
			
		||||
        <%= session 'SystemName' %>@<%= session 'DomainName' %></b> 
 | 
			
		||||
	</div>
 | 
			
		||||
        <div id="h2e22">
 | 
			
		||||
          <a target="_parent" href="manual">  <b> ? </b>  </a> 
 | 
			
		||||
	</div>
 | 
			
		||||
	<div id="h2e23">
 | 
			
		||||
%    if ( not defined $c->session->{username} ) {
 | 
			
		||||
          <a target="_parent" href="login"><b>Login</b></a> 
 | 
			
		||||
%    } else {
 | 
			
		||||
          <a target="_parent" href="logout"><b><%= $c->session->{username} %> Logout</b></a> 
 | 
			
		||||
%    }
 | 
			
		||||
	</div>
 | 
			
		||||
		<div id="h2e22">
 | 
			
		||||
			<a id="help-button" target="_parent" href="manual"><button> ? </button></a>
 | 
			
		||||
		</div>
 | 
			
		||||
    
 | 
			
		||||
        <div id="h2e12">
 | 
			
		||||
			<a id="legacy-button" href="/server-manager" target='_blank'><button>Legacy SM</button></a>
 | 
			
		||||
        </div>
 | 
			
		||||
		<div id="h2e23">
 | 
			
		||||
			% if ( not defined $c->session->{username} ) {
 | 
			
		||||
				<a target="_parent" href="login"><button class="login-button">Login</button></a>
 | 
			
		||||
			% } else {
 | 
			
		||||
				<a target="_parent" href="logout"><button class="login-button"><%= $c->session->{username} %> Logout</button></a>
 | 
			
		||||
			% }
 | 
			
		||||
		</div>
 | 
			
		||||
		<div id="flag-container">
 | 
			
		||||
			<!-- The flag icon will be inserted here -->
 | 
			
		||||
		</div>
 | 
			
		||||
    </div>
 | 
			
		||||
	<div id="h2l2" class="infobar"> 
 | 
			
		||||
		<div id="h2e21">
 | 
			
		||||
			<b>
 | 
			
		||||
				<%= session 'SystemName' %>@<%= session 'DomainName' %></b> 
 | 
			
		||||
		</div>
 | 
			
		||||
		
 | 
			
		||||
		
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -79,9 +79,9 @@
 | 
			
		||||
 | 
			
		||||
    	%=  hidden_field 'trt' => 'CONF'
 | 
			
		||||
	<br>
 | 
			
		||||
    <div class='center'>
 | 
			
		||||
    <!--<div class='center'>-->
 | 
			
		||||
	%= submit_button $c->l('SAVE'), class => 'action'
 | 
			
		||||
    </div>
 | 
			
		||||
    <!--</div>-->
 | 
			
		||||
 | 
			
		||||
    % end
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -47,9 +47,9 @@
 | 
			
		||||
 | 
			
		||||
    	%=  hidden_field 'trt' => 'INST'
 | 
			
		||||
	<br><br>
 | 
			
		||||
    <div class='center'>
 | 
			
		||||
    <!-- <div class='center'>-->
 | 
			
		||||
	%= submit_button $c->l('yum_INSTALL_SOFTWARE'), class => 'action'
 | 
			
		||||
    </div>
 | 
			
		||||
    <!--</div>-->
 | 
			
		||||
 | 
			
		||||
    % end
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -34,9 +34,9 @@
 | 
			
		||||
    	%=  hidden_field 'trt' => 'PSTU'
 | 
			
		||||
    	%=  hidden_field 'reconf' => $yum_datas->{reconf}
 | 
			
		||||
	<br>
 | 
			
		||||
    <div class='center'>
 | 
			
		||||
    <!-- <div class='center'>-->
 | 
			
		||||
	%= submit_button "$btn", class => 'action'
 | 
			
		||||
    </div>
 | 
			
		||||
    <!--</div>-->
 | 
			
		||||
 | 
			
		||||
    % end
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -46,9 +46,9 @@
 | 
			
		||||
 | 
			
		||||
    	%=  hidden_field 'trt' => 'REMO'
 | 
			
		||||
	<br>
 | 
			
		||||
    <div class='center'>
 | 
			
		||||
    <!--<div class='center'>-->
 | 
			
		||||
	%= submit_button $c->l('REMOVE'), class => 'action'
 | 
			
		||||
    </div>
 | 
			
		||||
    <!--</div>-->
 | 
			
		||||
 | 
			
		||||
    % end
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -33,9 +33,9 @@
 | 
			
		||||
 | 
			
		||||
    	%=  hidden_field 'trt' => 'UPDT'
 | 
			
		||||
	<br>
 | 
			
		||||
    <div class='center'>
 | 
			
		||||
    <!--<div class='center'>-->
 | 
			
		||||
	%= submit_button $c->l('yum_INSTALL_UPDATES'), class => 'action'
 | 
			
		||||
    </div>
 | 
			
		||||
    <!--</div>-->
 | 
			
		||||
 | 
			
		||||
    % end
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ Summary: Sme server  navigation module : manager 2
 | 
			
		||||
%define name smeserver-manager
 | 
			
		||||
Name: %{name}
 | 
			
		||||
%define version 11.0.0
 | 
			
		||||
%define release 9
 | 
			
		||||
%define release 18
 | 
			
		||||
Version: %{version}
 | 
			
		||||
Release: %{release}%{?dist}
 | 
			
		||||
License: GPL
 | 
			
		||||
@@ -108,6 +108,35 @@ true
 | 
			
		||||
%defattr(-,root,root)
 | 
			
		||||
 | 
			
		||||
%changelog
 | 
			
		||||
* Sun Aug 25 2024 Brian Read <brianr@koozali.org> 11.0.0-18.sme
 | 
			
		||||
- Move flag to emojii from downloaded jpg. Fix singleton locale issue[SME: 12706]
 | 
			
		||||
 | 
			
		||||
* Thu Aug 22 2024 Brian Read <brianr@koozali.org> 11.0.0-17.sme
 | 
			
		||||
- Left Align Software Install panels Submit button [SME: 12727]
 | 
			
		||||
 | 
			
		||||
* Wed Aug 21 2024 Brian Read <brianr@koozali.org> 11.0.0-16.sme
 | 
			
		||||
- Typo uc DNF changed to lc dnf in Yum.pm [SME: 127245]
 | 
			
		||||
- Monitor dnf running using dnf status file 
 | 
			
		||||
 | 
			
		||||
* Wed Aug 21 2024 Brian Read <brianr@koozali.org> 11.0.0-15.sme
 | 
			
		||||
- Migrate SM2 Software installer panel from use of yum to dnf [SME: 12718]
 | 
			
		||||
 | 
			
		||||
* Sun Jul 28 2024 Brian Read <brianr@koozali.org> 11.0.0-14.sme
 | 
			
		||||
- Version skipped due to operator error! [SME: <none> ]
 | 
			
		||||
 | 
			
		||||
* Sun Jul 28 2024 Brian Read <brianr@koozali.org> 11.0.0-13.sme
 | 
			
		||||
- Fix sysles.css template - overwrote it by mistake [SME: 12706]
 | 
			
		||||
- Also re-organised login and Legacy SM menus and help on top
 | 
			
		||||
 | 
			
		||||
* Sun Jul 28 2024 Brian Read <brianr@koozali.org> 11.0.0-12.sme
 | 
			
		||||
- correct positio of flag-icon [SME: 12706]
 | 
			
		||||
 | 
			
		||||
* Sat Jul 27 2024 Brian Read <brianr@koozali.org> 11.0.0-11.sme
 | 
			
		||||
- Add in flag icon indication of locale [SME: 12706]
 | 
			
		||||
 | 
			
		||||
* Fri Jul 26 2024 Brian Read <brianr@koozali.org> 11.0.0-10.sme
 | 
			
		||||
- fix navigation2.conf to more correctly translate menus [SME: 12714]
 | 
			
		||||
 | 
			
		||||
* Thu May 09 2024 Brian Read <brianr@koozali.org> 11.0.0-9.sme
 | 
			
		||||
- Add mojo logo to footer [SME: 12679]
 | 
			
		||||
- Fix default for HeaderWeight to avoid noise in logs if no Nav header in file
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user