CopyTo List Attachment to Shared Documents

by DeanLogic

As part of one of my projects at work, I was tasked to modify a form for a list to have the Attachment moved to the Document Library. When the file is in the Document Library, I can then apply all sorts of change control and stuff to the document. Being new to playing around with jQuery and JSOM in SharePoint, I searched the web for an example on how to do this action. I found that there was a method called copyTo that would do what I needed. Great! Now how do I use it?

I searched around and even found a site that said

Note: If you want to copy files between two libraries on the same level, then you can use the copyTo method.

http://server/site/_api/web/folders/GetByUrl('/site/srclib')/Files/getbyurl('madcow.xlsx')/copyTo(strNewUrl = '/site/targetlib/madcow.xlsx,bOverWrite = true)

Okay, that seems pretty straight forward and not informative enough. I even tried to look at his code for copying to a different site to see if a light bulb would go off in my head. Looking on SharePoint StackExchange wasn’t yielding any positive results, so I posted a question myself (Forbidden error when trying to copyTo document from list to Document Library) when attempts to get the coding to work kept failing. As with all road blocks, the best option is to step back, go do something else to clear your head and then come back to the problem.

I (by accident) deleted all my saved tabs in my browser, so I had to do a new search to find the pages I had up. When I did this search, I found a question in SharePoint StackExchange; Automatically add attachment to new list item. At first I didn’t think it would help me, but then after looking at it for a while, I decided to give it a try. I ended up getting a better error and then, presto!! It worked! Woo hoo!

So, that I can find this for later and hopefully help someone else out, I am going to post the answer here and on the StackExchange.

Note: The getUrlParameter(“ID”) is a short function that splits out the page URL and retrieves the value for the parameter. Someone else created it and it works perfectly.

	function getQueryStringParameter(paramToRetrieve) {
		var params = document.URL.split("?")[1].split("&");
		var strParams = "";

		for (var i = 0; i < params.length; i = i + 1) {
		  var singleParam = params[i].split("=");
		  console.info("singleParam : " + singleParam);
		  if (singleParam[0] == paramToRetrieve)
			return singleParam[1];
		}
	}

First, create the function to get the current list and current item in order to find the attachment as the file object.

    function justCopyIt() {
    		thisClientContext = new SP.ClientContext.get_current();    
    		this.web = thisClientContext.get_web();
    		this.sourceList = this.web.get_lists().getByTitle("Memo");
    		thisClientContext.load(this.sourceList); 
    		this.currentItem = sourceList.getItemById(getUrlParameter("ID"));
    		thisClientContext.load(this.currentItem);
    		
    		var attachmentFolder = this.web.getFolderByServerRelativeUrl("/sites/MySite/Lists/Memo/Attachments/" + getUrlParameter("ID") + "/");	
    		sourceFile = attachmentFolder.get_files();
    		
    		thisClientContext.load(sourceFile);
    		thisClientContext.executeQueryAsync(Function.createDelegate(this, onQuerySucceeded), Function.createDelegate(this, onGetFail));
    	}

After the source file is loaded, call the executeQueryAsync to run the query.
I have a onGetFail function that I handle all failed query calls. Don’t forget to create one of your own.

    function onQuerySucceeded(sender, args) {
    		if (sourceFile != null) {
    
    			destinationlibUrl = "http://myServer/sites/MySite/Shared%20Documents/" + $.local.arrFileName;      
    			sourceFile.itemAt(0).copyTo(destinationlibUrl, 1);
    
    			notifyId = SP.UI.Notify.addNotification("Copying file " + sourceFile.itemAt(0).get_serverRelativeUrl() + " to " + destinationlibUrl, true);
    			thisClientContext.executeQueryAsync(
    				function (sender, args) {
    					SP.UI.Notify.removeNotification(notifyId);
    					SP.UI.Notify.addNotification('File copied successfully', true);          
    				},
    				function (sender, args) {
    					 SP.UI.Notify.addNotification('Error copying file', false);
    					 SP.UI.Notify.removeNotification(notifyId);
    					 console.error('Error occured: ' + args.get_message());
    				}
    			);
    		}
    	}

When the onQuerySucceed function runs, it does the copyTo and adds a on screen notification to let the user know the “Copying File” has started and when it is successful.

Seems so simple when it works.

Vector Wars – game review

by DeanLogic Passport

Vector Wars app Since I had a little time to kill, I downloaded a game from  BlackBerry World.

Vector Wars is a space ship shooting game that harks back to the days of Asteroids and Space War with a hint of Wing Commander. You pilot a small vector shaped ship (hence the name) and battle off waves of enemy ships. The other part of the game is being verbally abused by commander of the supply ship.

Vector Wars play screen

The game starts off with a tutorial level and plenty of abuse from the supply commander. The tutorial is needed if you aren’t familiar with playing a two controller game on your phone. First thing is, you have to put two fingers on the screen to have both controllers. The left controller handles the ship movement. Controlling the ship takes a little getting used to. Playing on my Passport probably gives a different feeling than using a Z30. I can’t imagine playing it on a Q10 or Classic.

The right controller fires the weapon and points the ship. Therefor, when you have a gun of limited ammo, don’t keep your finger on the screen. As hinted at, you can get different weapons, which are dropped off by the supply ship throughout the game. Having the left controller figured out helps you retrieve the glowing crates with power ups, lives and weapons.

As with other shooting games, each level has increasingly tougher ships and the ships do different things. Either fly at you or shoot at you or maybe a combination. After a few levels, there is a boss ship, which can only be defeated by shooting at it; to the dismay of those who were just ramming the smaller ships.

After you die too many times, you are sent back to the home screen. From there, your points start over, but not necessarily the level. “Wait a minute! How to I build points back up?” Good question, other voice in my head.

After you are back at the home screen and hit “Play”, you are sent to levels screen.
IMG_20150506_132608

On the levels screen, there is a slider. It’s position is at your last played level. Just slide it back to start building your score back up.
Vector Wars levels

It is a fun and challenging and deceptively simple game. While it looks like a 2D game, it is actually in 3D. Beware that those 3D graphics can suck up your battery.
You can get Vector Wars and other crazy games by FileArchiveHaven in BlackBerry World.

jQuery and SharePoint id

by DeanLogic Passport

It has been a while since I’ve posted anythin new, this because the new focus for programming has been SharePoint and jQuery at my new job.

I now work for CACI as a sub-contractor on a government job. They mainly use ColdFusion and HTML for their pages, but are currently switching over to SharePoint 2013. And by switching over, I mean redoing Lotus Notes apps and other non-web apps onto SharePoint. Being a government job, also means that SharePoint is locked down in ways that makes customization limited. Apparently to get around such limitations, Microsoft has provided JSOM to access the list data and you can use JavaScript and jQuery to modify the Pages. Fun. Fun. Fun.

There are a lot of examples for JSOM and jQuery, so I am not sure what I will post about it in the future. Maybe if I figure out something worthy, I will make a post. Right now, I am just going to post something that I find annoying.

SharePoint isn’t one of my favorite programs, but knowing how to use and manipulate it helps pay the bills. When you want to do anything in SharePoint, you add a list (which they have renamed to “apps” in 2013). To add or edit things in the list, you normally use a form. SharePoint builds these automatically based off of the columns in the list. Fairly straight forward process. Normally, if you wanted to modify the form, you would use InfoPath. But due to restrictions, license, end-of-life, etc… that’s no longer an option. This is where jQuery comes in.

A form for a list is actually a Page. And since it is a Page, you can edit it in the browser and add a Web Part. If you add a Script Editor web part, you can embed JavaScript, jQuery, CSS and HTML. Just remember to do it for the New, Edit and View forms, because they are 3 different Pages. When you want to do something that modifies a form element, then here comes the issue with SharePoint.

Form elements have the “id” property to help finding it on the page. SharePoint creates the “id” property using a combination of the column name and a long alpanumeric value. Good luck figuring that out.

With jQuery, you can find an element, but you need a starting point. For the moment, you can find the No Break <nobr> tag that wraps each for fields Title. From there, you can find the nearest table row <tr> and then finally find the element you are looking for with a Title similar the column name. Required columns are appeneded with “Required Field”, so you can’t look for an exact match.

<br />
$(&quot;nobr:contains('MY Title')&quot;).closest(&quot;tr&quot;).find(&quot;input[title*='MY Title']&quot;);<br />

You could just do the find part, if you were sure there wouldn’t be a similar Title, but it is probably best to narrow it down.

At this point, I could get the “id” property, but that is apparently pointless. Just add another “.” and do whatever you need; listener function, css, value, etc…

Lesson learned the hard way.

Cascades animations in games

by Roger Leblanc

This article is a follow-up of How to use Cascades built-in animations

Animations are perfect for simple games, so let’s create one right now. The game we’ll be building is called ‘No Fly Zone’ and consist of a fly flying around the screen that we need to kill. First, we’ll need a fly image, so I’ve found this one from http://www.openclipart.org :

Fly

 

We’ll separate this image in two parts : the body and wings. Save those 2 images in ‘assets/images/’ folder (you’ll need to create ‘images’ folder).

fly_wingsfly_body

To make the wings flap, we’ll put both images on top of each other and then set a small back and forth rotation transition to the wings. Let’s create a custom component for that fly and name it FlyImage.qml :

 

// FlyImage.qml
import bb.cascades 1.0

Container {
    id: flyContainer
    layout: DockLayout {}
    property int angleForWingsFlapping: 20

    maxHeight: 100
    maxWidth: maxHeight
    
    ImageView {
        imageSource: &amp;amp;quot;asset:///images/fly_wings.png&amp;amp;quot;
        onCreationCompleted: {
            wingsFlappingAnimation.play()
        }
        animations: [
            SequentialAnimation {
                id: wingsFlappingAnimation
                repeatCount: AnimationRepeatCount.Forever
                RotateTransition {
                    // Rotate wings clockwise slightly
                    toAngleZ: angleForWingsFlapping
                    duration: 2
                }
                RotateTransition {
                    // Rotate wings counterclockwise slightly
                    toAngleZ: 0 - angleForWingsFlapping
                    duration: 2
                }
            }
        ]
    }
    
    ImageView {
        id: flyBodyImage
        imageSource: &amp;amp;quot;asset:///images/fly_body.png&amp;amp;quot;
    }
}

Now, in main.qml, we can add this custom component by calling its name, and we’ll have a fly with flapping wings on the screen :

// main.qml
import bb.cascades 1.0

Page {
    content: Container {
        FlyImage {}
    }
}

Now that we have a fly with animated wings, let’s move that fly around the screen :

 

// main.qml
import bb.cascades 1.0

Page {
    id: mainPage
    
    // You may need to change deviceWidth and deviceHeight values depending on your device
    property int deviceWidth: 1440
    property int deviceHeight: 1440
    property int speed: 3000
    
    onCreationCompleted: {
        // Starts the animation when the app launch
        thisAnimation.play()
    }
    
    content: Container {
        layout: DockLayout {}
        background: Color.LightGray
        horizontalAlignment: HorizontalAlignment.Fill
        verticalAlignment: VerticalAlignment.Fill
        
        FlyImage {
            id: flyImageAlive
            
            maxHeight: 100
            maxWidth: maxHeight
            
            // Starts at the bottom of the screen                        
            translationX: Math.random() * deviceWidth 
            translationY: deviceHeight + 100
            
            animations: [
                ParallelAnimation {
                    id: thisAnimation
                    
                    onEnded: {
                        // This is what happens when one animation cycle is done
                        
                        // Recalculate new translation points
                        thisTranslation.toX = Math.random() * deviceWidth
                        thisTranslation.toY = Math.random() * deviceHeight
                        
                        // Recalculate new scale transition value
                        thisScale.toX = (Math.random() * 1.5) + 0.5
                        thisScale.toY = thisScale.toX
                        
                        // Restart the animation with the new values
                        thisAnimation.play()
                    }
                    
                    TranslateTransition {
                        // Move the fly toX and toY values
                        id: thisTranslation
                        duration: speed
                        easingCurve: StockCurve.Linear
                        toX: Math.random() * deviceWidth
                        toY: Math.random() * deviceHeight
                    }
                    
                    ScaleTransition {
                        // Rescale the fly randomly
                        id: thisScale
                        duration: speed
                        toX: (Math.random() * 1.5) + 0.5
                        toY: toX
                    }
                }
            ]
        }
    }
}

So now we have a fly flying randomly around the screen. Only thing left to do is handling tap event for when the user hit the fly and creating a score board, and there you have a simple game using Cascades built-in animations.

Complete source code is available on GitHub

You can find ‘No Fly Zone’ free game in BlackBerry World at :
http://appworld.blackberry.com/webstore/content/59946084

How to use Cascades built-in animations

by Roger Leblanc

Nowadays, there’s a lot of mobile app developers and your apps need to stand out to catch the user’s attention. Most users like to interact with the app, and animations can bring this interaction to another level, not only letting the user touch a component on the screen and have a reaction, but also having this component to move, scale, fade or rotate to create a real sense of interaction and fluidity.

Hopefully, Cascades makes it easy to use animations with only a few lines of code.

 

IMPLICIT AND EXPLICIT ANIMATIONS

Cascades support 2 types of animations, implicit and explicit animations. Implicit animations doesn’t need any coding and can be used on 2 types of properties :

  • Properties that determine how a control looks, such as rotation, translation, and opacity
  • Properties that determine the layout of a control in a container, such as preferred width and preferred height

So if you set the opacity of a Button to 0, it will fade out gradually instead of becoming invisible right away. Cascades takes care of the animation, you don’t have to write any line of code for this fade out animation.

Here’s some sample code to test implicit animations :

import bb.cascades 1.0

Page {
    
    content: Button {
        
        text: "Click me"
        
        onClicked: {
            
            translationX += 20
            
            opacity -= 0.2
            
            if (opacity == 0)
                
                opacity = 1
        
        }
    
    }

}

EXPLICIT ANIMATIONS : FADE, ROTATE, TRANSLATE, SCALE

If you want to have more control on the animation used, then you’ll want to look into explicit animations, where you can set from and to values, duration, easing curve, a delay before the animation start or number of repeat for your animation. Let’s take a fade transition, we want a Button to fade slowly from 1.0 to 0.0 and make it repeat forever :

import bb.cascades 1.0

Page {
    
    content: Container {
        
        Button {
            
            text: "Click me"
            
            animations: [
                
                FadeTransition {
                    
                    id: fadeTransition
                    
                    duration: 3000
                    
                    fromOpacity: 1.0
                    
                    toOpacity: 0.0
                    
                    repeatCount: AnimationRepeatCount.Forever
                
                }
            
            ]
            
            onClicked: {
                
                fadeTransition.play()
            
            }
        
        }
    
    }

}

So we have a better control of the animation with an explicit animation. There are 4 types of animations that can be controlled with explicit animation :

  • Fade transition
  • Rotate transition
  • Translate transition
  • Scale transition

 

GROUP ANIMATIONS

It’s even possible to group animations together and make them perform one after the other or all at the same time. Those are called SequentialAnimation and ParallelAnimation. Let’s try both to see the difference.

import bb.cascades 1.0

Page {
    content: Container {
        Button {
            text: "Click me"
            
            animations: [
                ParallelAnimation {
                    id: parallelAnimation
                    
                    animations: [
                        TranslateTransition {
                            toX: 400
                            duration: 2000
                        },
                        RotateTransition {
                            toAngleZ: 180
                            duration: 2000
                        }
                    ]
                }
            ]
            
            onClicked: {
                parallelAnimation.play();
            }
        }
        Button {
            text: "Click me"
            
            animations: [
                SequentialAnimation {
                    id: sequentialAnimation
                    
                    animations: [
                        TranslateTransition {
                            toX: 400
                            duration: 2000
                        },
                        RotateTransition {
                            toAngleZ: 180
                            duration: 2000
                        }
                    ]
                }
            ]
            
            onClicked: {
                sequentialAnimation.play();
            }
        }
    }
}

For now, all the animations used were a little bit useless, but they showed possibilities. For a real life example, let’s say we have a freemium app and we want to invite the user to upgrade to Pro version, we can do it like this :

import bb.cascades 1.0

Page {
    property int deviceWidth: 1440 // Set it to your screen width
    property int deviceHeight: 1440 // Set it to your screen height
    content: Container {
        layout: DockLayout {}
        horizontalAlignment: HorizontalAlignment.Fill
        verticalAlignment: VerticalAlignment.Fill
        Container {
            id: mainAppContainer
            Label {
                text: "This is your main app"
            }
            Container {
                topPadding: 30
                Button {
                    text: "Random action that prompt user to upgrade"
                    onClicked: upgradeContainer.visible = true
                }
            }
        }
        Container {
            id: upgradeContainer
            background: Color.Black
            horizontalAlignment: HorizontalAlignment.Fill
            verticalAlignment: VerticalAlignment.Fill
            opacity: 0.7
            visible: false
            onVisibleChanged: {
                if (visible) {
                    upgradeButton.translationX = deviceWidth // Make it out of sight
                    upgradeButton.translationY = deviceHeight // Make it out of sight
                    noThanksButton.translationX = deviceWidth // Make it out of sight
                    noThanksButton.translationY = deviceHeight // Make it out of sight
                    
                    upgradeButtonAnimation.play()
                    noThanksButtonAnimation.play()
                } 
            }
            Button {
                id: upgradeButton
                text: "Upgrade"
                preferredWidth: 300
                preferredHeight: 30
                onClicked: upgradeContainer.visible = false
                animations: [
                    ParallelAnimation {
                        id: upgradeButtonAnimation
                        RotateTransition {
                            fromAngleZ: 160
                            toAngleZ: 0
                            duration: 1500
                            easingCurve: StockCurve.ElasticOut
                        }
                        TranslateTransition {
                            fromX: deviceWidth * 0.75
                            fromY: deviceHeight
                            toX: (deviceWidth / 2) - (upgradeButton.preferredWidth / 2)
                            toY: (deviceHeight / 2) - (upgradeButton.preferredHeight / 2)
                            duration: 1500
                            easingCurve: StockCurve.ElasticOut 
                        }
                    }
                ]
            }
            Button {
                id: noThanksButton
                text: "No Thanks"
                preferredWidth: 300
                preferredHeight: 30
                onClicked: upgradeContainer.visible = false
                animations: [
                    ParallelAnimation {
                        id: noThanksButtonAnimation
                        delay: 4000 // Setting a delay will make this button appear later
                        RotateTransition {
                            fromAngleZ: 160
                            toAngleZ: 0
                            duration: 1500
                            easingCurve: StockCurve.ElasticOut
                        }
                        TranslateTransition {
                            fromX: deviceWidth * 0.75
                            fromY: deviceHeight
                            toX: (deviceWidth / 2) - (noThanksButton.preferredWidth / 2)
                            toY: (deviceHeight / 2) - (noThanksButton.preferredHeight / 2) + upgradeButton.preferredHeight
                            duration: 1500
                            easingCurve: StockCurve.ElasticOut
                        }
                    }
                ]
            }
        }
    }
}

What’s happening here is when a certain action is performed by the user, it shows a semi-transparent black screen (opacity 0.7) and then 2 buttons comes from out of sight to the middle of the screen, an ‘Upgrade’ button and a ‘No Thanks’ button. User have to click either one to continue to use the app.

Animations can be a lot of fun in Cascades, it’s powerful, simple to code and look nice. It allows your app to stand out from the crowd, so go ahead, and play with it!

Reference : https://developer.blackberry.com/native/documentation/cascades/ui/animations/index.html