≡ Menu
Oracle Primavera P6 EPPM

I support an environment where users access the Primavera P6 thick client on Windows via Citrix.

If I connect to the Database Configuration app and add, modify, or delete the connection to a P6 database, the PrmBootStrap.xml file that is in my profile. For the version we are using (8.2), that location is C:\Users(username)\AppData\Local\Oracle\Primavera P6\P6 Professional. It appears that the file is also copied to the “%PROGRAMDATA%\Oracle\Primavera P6\P6 Professional” folder and the “All Users” profile (if it exists) as well, in “C:\Users\All Users\AppData\Local\Oracle\Primavera P6\P6 Professional”.
In the code below, I copy from the ProgramData folder instead of from my profile, but it would be easy to eliminate the outer for loop and uncomment two lines to make it copy from my profile.

Since I have multiple Citrix servers for P6, I normally would manually copy the file to the folders on the other Citrix servers so that the users will have the same list no matter where they log on. To simplify this, I wrote this Windows batch script to automate this copy process.

@echo off
cls
setlocal EnableDelayedExpansion
rem //Citrix servers hosting Primavera P6 should be in the serverlist variable, separated by spaces
set serverlist=citrixserver1 citrixserver2 citrixserver3
rem //Windows username of person who has standard file
set usersname=foobar

set programdataunc=%PROGRAMDATA::=$%
set filepath=Oracle\Primavera P6\P6 Professional
set filename=PrmBootStrap.xml

rem set Sourceloc="C:\Users\%usersname%\AppData\Local\%filepath%\%filename%"

for %%s in (%serverlist%) do (	
	set Sourceloc="\\%%s\%programdataunc%\%filepath%\%filename%"
	
	if not exist !Sourceloc! (
		echo No file found: !Sourceloc!
	) else (
		for %%a in (!Sourceloc!) do set SourceFileDate=%%~ta

		set offset=0
		if "!SourceFileDate:~-2!" == "PM" set offset=12					
		set /a "SourceFileDateHour=!SourceFileDate:~11,2!+!offset!"
		set "SourceFileDateCompare=!SourceFileDate:~6,4!!SourceFileDate:~0,2!!SourceFileDate:~3,2!!SourceFileDateHour!!SourceFileDate:~14,2!"					

		for %%x in (%serverlist%) do (
			echo.			
			echo DestinationServer = %%x
			echo SourceServer = %%s
			echo .
			if not %%x==%%s (
				set firstloc="\\%%x\%programdataunc%\%filepath%\%filename%"
				set secondloc="\\%%x\C$\Users\All Users\%filepath%\%filename%"
				
				for %%y in (!firstloc! !secondloc!) do (			    
					if exist %%y (						
						echo =-=-=-=-=-=
						for %%a in (%%y) do set DestinationFileDate=%%~ta
						set offset=0
						if "!DestinationFileDate:~-2!" == "PM" set offset=12					
						set /a "DestinationFileDateHour=!DestinationFileDate:~11,2!+!offset!"
						set "DestinationFileDateCompare=!DestinationFileDate:~6,4!!DestinationFileDate:~0,2!!DestinationFileDate:~3,2!!DestinationFileDateHour!!DestinationFileDate:~14,2!"							
						echo DestinationFileDateCompare = !DestinationFileDateCompare!
						echo SourceFileDateCompare = !SourceFileDateCompare!
						echo .
						if "!SourceFileDateCompare!" gtr "!DestinationFileDateCompare!" (					    
							set NewDestinationFileName=!filename!_!DestinationFileDateCompare!
							echo !NewDestinationFileName!
							ren %%y !NewDestinationFileName!
							copy !Sourceloc! %%y						
						) else (							
							echo Destination file %%y is newer than or the same as !Sourceloc!.
						)
						echo =-=-=-=-=-=
						echo.
					) else (
						echo.
						echo =x=x=x=x=x=
						echo INFO: %%y not found					
						echo =x=x=x=x=x=
						echo.
					)  
				)
			) else (
				echo Destination and Source servers are the same: no file copied.
			)
		)
	)
)
rem pause

If you uncomment the pause command at the end, you can look at the command window to make sure it’s working properly.

Stripping HTML Tags from Textboxes Using JavaScript

HTML tags

One of the applications I support routinely has a problem where users cut and paste text that contains HTML tags from other sources into the text boxes on entry forms in my application. Most of the time, this does not cause a problem. However, if the users do not capture all of the text, they may unwittingly not grab the closing tags. When that happens, all sorts of fun things can happen to the main page on the app, which shows recent entries.

I decided I’d like to remove all HTML, except I’d like to retain line breaks. Based partially on a solution I found on Stack Overflow, I made this function.

this.fixHtml =
	function(strHtml)
	{
		var div = document.createElement('div');
		strHtml = strHtml.replace(/<br>/gi,'\n');
		strHtml = strHtml.replace(/<br \/>/gi,'\n');
		div.innerHTML = strHtml;
		strHtml = div.innerText || div.textContext;
		strHtml = strHtml.replace(/\n/gi,'<br />');
		div.innerHTML = strHtml;			
		return div.innerHTML;
	};

I call the fixHtml function whenever an input tag is of type text, or if a div element is of the class “mimicTextArea” (it looks like a text box):

if(oForm!=null)
{
	var oInputs = oForm.getElementsByTagName("input");
	for(var x=0;x<oInputs.length;x++)
	{
		if(oInputs[x].type=="text" && oInputs[x].value.length > 0)
		{
			oInputs[x].value = self.fixHtml(oInputs[x].value);
		}
	}
	
	var oDivs = oForm.getElementsByTagName("div");
	for(var x=0;x<oDivs.length;x++)
	{
		if(oDivs[x].className == "mimicTextArea" && oDivs[x].innerHTML.length > 0)
		{
			oDivs[x].innerHTML = self.fixHtml(oDivs[x].innerHTML);
		}
	}
	
}

This seems to serve my purpose well. Should I wish to retain other specific HTML tags, a similar technique may be used as above if they are self-closing tags such as the line break tag. Tags that require an opening and a closing tag would require further development.

Problems with Installing Applications on Windows Servers via Remote Desktop

Terminal Server remote client not allowed dialog box

If you have tried to install an application on a Windows Server when logged in with Remote Desktop Connection and the result was a dialog box like the one above, this post is for you.

Understandably, many software companies want different kinds of licensing to be used when installing on a server running Terminal Services. The assumption is that you will likely be using it as a Citrix server, allowing many users to access the application at once. If this is what you are intending to do, then you must follow the licensing scheme required by the software vendor.

However, what if your intention is to install it only for yourself? In my case, I have a virtual development server that no one else uses, but I log onto it with Remote Desktop. When trying to install an application the other day, I got a message similar to the one above. How does it know that I’m logging on remotely?

Other users have had similar experiences, and some of the suggestions on this post on SuperUser gave me an idea.

What if I was logged onto the Console rather than an RDP session? This is a virtual server that is not physically located in the same place as me, so that seemed unlikely. Then I remembered VNC! After installing a free copy of UltraVNC Server on the server and Viewer on my PC, I was able to log in as an apparent session from the Console.

The application was satisfied and allowed me to install it without any further complications.

CSS variable declaration and use

About three years ago, I wrote a post about dynamically resizing the content column in WordPress.org when using the Thesis theme.

The method described in that post worked fine until Thesis 2.4 was released last August. At that time, I noticed that my columns were no longer the full width of the window, and did not change size as I expanded or shrank the window. I sent an email to Thesis support and was told that a Box called Fix CSS would likely fix my problem. I installed it, but to no avail. Upgrading to Thesis 2.6 last month also had no effect.

Last week I decided to send another email and got a little more information. The creator of Thesis responded. Part of the message is below.

“Thesis 2.6 has ‘clarified’ the way you can use variables. Here’s how it works…

DO NOT:
Modify the Skin CSS, Editor CSS, or CSS Variables that come with your Thesis Skin. Whenever a Skin is updated, these three items will be overwritten, and you will lose any changes you’ve made to them.

DO:
Isolate ALL your CSS changes to the Custom CSS interface. Your modifications will be safe here during future Skin updates.
Create inline variables inside your Custom CSS interface, as seen in the image below:

picture of Custom CSS screen in Thesis

You can REFERENCE existing CSS Variables in your Custom CSS code, and you can also create and reference your own variables (like those shown in the image above) here as well.”

Now I had to implement the same steps as before in the previous post, but using this new method.

While the changes causing the builtin variables to revert to their original values may have been implemented in 2.6, my code had quit working at 2.4. So there’s a mystery! First, I removed the custom code and deleted the new variables from the Skin CSS so that it would be as it would directly downloaded the Thesis website.

Then, I basically set up new variables in the Custom CSS using the syntax shown in the screenshot above. I hit a snag here, however. For whatever reason, the CSS engine no longer supported the CSS calc() function! Also, I discovered that some of the CSS class names I originally used seemed not to affect the style of the column any longer.

The code below, which I appended to the bottom of my Custom CSS, does several things. First, it declares variables for the Custom CSS page in Thesis. Secondly, it implements those custom variables on the appropriate CSS classes and overrides the Skin CSS in all places where the variables $w_total and $w_content were used. Lastly, it declares a CSS variable to store the value in the builtin $w_sidebar Thesis variable. This will be useful in the next step, in which I will use a workaround for the lack of a working CSS calc() function.

$w_total_new = 80%;
$w_total_min = 897px;
$w_content_min = 585px;

.container, .columns > .content{
	max-width: $w_total_new;
	width: auto;	
}

.landing .container{	
	min-width: $w_content_min;
}

.full_width{
	min-width: $w_total_new;
}

.columns > .sidebar {	
	max-width: $w_sidebar;
}

:root{
	--sidebar: $w_sidebar;	
}

Since the modified $w_content variable in the original post used calc(), we’ll have to use the magic of JavaScript and jQuery to change the CSS after the page is loaded.

First, I grab the CSS variable called “–sidebar” by combining the window.getComputedStyle() method and the CSSStyleDeclaration.getPropertyValue() method interface.

I then trim the returned value to eliminate whitespace, and concatenate the string “-=” with the sidebar value which, incidentally, is currently 312px by default.

Then, I set the CSS to 100% for the classes listed. I then subtract (hence the “-=”) the sidebar width.

This:

$w_content = calc(100% - $w_sidebar);

Becomes this:

var sidebar = '-=' + window.getComputedStyle(document.body).getPropertyValue('--sidebar').trim();
if (sidebar==='-=') {
	sidebar = '-=' + window.getComputedStyle($('.sidebar')[0]).getPropertyValue('max-width').trim();
}
$('.landing .container, .columns > .content').css('width', '80%').css('width', sidebar);	 

Lastly, I wrapped it in a jQuery function that runs only after the window (including the CSS files) is fully rendered. The completed code below may be put in a Custom HTML widget in WordPress.

<script>
(function($) {
$(window).load(function() {	
	var sidebar = '-=' + window.getComputedStyle(document.body).getPropertyValue('--sidebar').trim();
	if (sidebar==='-=') {
		sidebar = '-=' + window.getComputedStyle($('.sidebar')[0]).getPropertyValue('max-width').trim();
	}
	$('.landing .container, .columns > .content').css('width', '100%').css('width', sidebar);	 
	$('img').each(function(){ 
		var image = $(this); 
		if(image.context.naturalWidth === 0 || image.readyState === 'uninitialized'){  
		$(image).unbind('error').hide();
      }	
	});	
});
})(jQuery);
</script>

Voila! The content column is once again resizable!

Nota bene: This has only been tested on the Social Triggers skin for Thesis. Other skins may use different CSS classes, so YMMV.