How to give a JIRA User access to only one project

POSTED ON June 17th  - POSTED IN JIRA

How to give a JIRA User access to only one project?

This is way more confusing than it needs to be, but here’s how I was able to grant a user access to only one project in JIRA.

Step 1: Create a new user in the User Management section of the Administrator. For this example, we’re just using username: test and email: test@mobileaws.com. Now, remove all of the Groups from this user. This means remove Jira-User and Jira-Developer. Jira-User will give the user access to all projects, which is not what we want. We should now have a user who can’t even logint o JIRA good.

Create New JIRA  User

Create New JIRA User

Step 2: Create a new group in the Administrator section. Entitle this new group TestGroup.

Create a Jira Group for Single Project Access

Create a Jira Group for Single Project Access

Step 3: Navigate to Jira\Administration\Global Permissions.

JIRA Administrator Global Permissions

JIRA Administrator Global Permissions

Step 4: Add your Test Group to Permission JIRA Users

Add Group to Permission JIRA Users

Add Group to Permission JIRA Users

Step 5: Your TestGroup should show up under JIRA Users

Single Project Access JIRA Users

Single Project Access JIRA Users

Step 6: Navigate back to the User Management Administration setting. Find the new user. Add only the TestGroup to the new user Test.

Add Single Access Group to New JIRA User

Add Single Access Group to New JIRA User

Step 7: Navigate to your Project in the Administrator mode. Add your new Group to one of the existing Roles, e.g. JIRA Users.

Add Single Project Group to JIRA Project Roles

Add Single Project Group to JIRA Project Roles

Step 8: Logout of JIRA as Administrator. Log back in with the new User. You should only have access to your Project!

Amazon SNS Push Notification Tutorial on Android using GCM

POSTED ON March 25th  - POSTED IN Android, AWS, Mobile

Amazon SNS Push Notification Tutorial on Android using GCM

Do you have a project where an Android device needs to receive push notifications from an endpoint registered on Amazon Simple Notification Service (SNS)? Amazon has a sample Android app, but, unfortunately the only thing this sample app does is register with Google Cloud Messaging (GCM). You can find the sample app here, SNSMobilePush.zip.

There are a lot more steps involved to enable your device to receive push notifications from an Amazon endpoint. Let’s get started.

First, we need to register the device to GCM. For that, we call a task that will handle the registration in the onCreate event of our first activity:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class MyActivity extends Activity{
 
    ….
 
    @Override
 
    protected void onCreate(Bundle savedInstanceState) {
 
        super.onCreate(savedInstanceState);
 
 
 
        //let's register this app to the gcm if it hasn't been registered already.
 
        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
 
        String gcmToken = sp.getString(getString(R.string.gcm_pref_token), null);
 
        GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(getApplicationContext());
 
        if(TextUtils.isEmpty(gcmToken)){
 
            new GCMRegisterTask(this, gcm).execute();
 
        }
 
    }
 
}

Now we need the Task that is going to actually register our App to the GCM. The string R.string.gcm_project_number is the Project number given to you by the google console when you register your project:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class GCMRegisterTask extends AsyncTask<String, Void, Boolean> {
 
    private Context context;
 
    private GoogleCloudMessaging gcm;
 
    public GCMRegisterTask(Context context, GoogleCloudMessaging gcm){
 
        super();
 
        this.context = context;
 
        this.gcm = gcm;
 
    }
 
    @Override
 
    protected Boolean doInBackground(String... params) {
 
        String token;
 
        try {
 
            token = gcm.register(context.getString(R.string.gcm_project_number));
 
            SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
 
            sp.edit().putString(context.getString(R.string.gcm_pref_token), token).apply();
 
            return true;
 
        }
 
        catch (IOException e) {
 
            Log.i("Registration Error", e.getMessage());
 
        }
 
        return false;
 
    }
 
}

Keep the token received from the GCM on your preferences. You’re going to need it when creating the endpoint on the Amazon SNS.

Now the device has been registered and is able to receive push notifications thru GCM, but we still need to map our GCM account to a SNS endpoint. The GCM is unique for the device. The SNS, however, is where you’ll filter your push notifications by using the customData property. So, if you’re registering for your users, you’ll probably want to register it on login and unregister it on logout.

To create an endpoint on the login, we can use a task:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
public class AWSCreateEndpointTask extends AsyncTask<String, Void,  CreatePlatformEndpointResult> {
 
    Context context;
 
    public AWSCreateEndpointTask(Context context ){
 
        super();
 
        this.context = context;
 
    }
 
    @Override
 
    protected CreatePlatformEndpointResult doInBackground( String[] params ) {
 
        if(params.length < 3){
 
            return null;
 
        }
 
        String arn = params[0];
 
        String gcmToken = params[1];
 
        String userData = params[2];
 
        try {
 
            CreatePlatformEndpointRequest request = new CreatePlatformEndpointRequest();
 
            request.setCustomUserData(userData);
 
            request.setToken(gcmToken);
 
            request.setPlatformApplicationArn(arn);
 
            return AWSManager.getSNSClient().createPlatformEndpoint(request);
 
        }catch(Exception ex){
 
            return null;
 
        }
 
    }
 
    @Override
 
    protected void onPostExecute(CreatePlatformEndpointResult result) {
 
        if(result != null) {
 
        SharedPreferences prefs = context.getSharedPreferences( “my_prefs” , Context.MODE_PRIVATE );
 
            String endpointArn = result.getEndpointArn();
 
            prefs.edit().putString( context.getString(R.string.endpoint_arn), endpointArn ).apply();
 
        }
 
    }
 
}

This task received 3 parameters to run. Arn is the code you receive for the GCM endpoint when creating your GCM integration on Amazon. GCMToken is the token we got before and stored on the preferences. And userData is the data we will use to filter the push notifications on our backend.

So, to run this task, we can do something like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
String token = PreferenceManager.getDefaultSharedPreferences(this).getString( 
 
this.getString(R.string.gcm_pref_token), null );
 
        if(!TextUtils.isEmpty(token)) {
 
            new AWSCreateEndpointTask(this).execute(
 
                    “arn:aws:sns:region:XXXXXXXXXXXX:app/GCM/AppName”
 
                    token,
 
                    email);
 
        }

After registering the endpoint on SNS, you need to keep the arn so you can, later, unregister it. It’s easy to remove an endpoint once you have the arn from the endpoint:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class AWSRemoveEndpointTask extends AsyncTask<String, Void, Boolean> {
 
    Context context;
 
    public AWSRemoveEndpointTask(Context context ){
 
        super();
 
        this.context = context;
 
    }
 
    @Override
 
    protected Boolean doInBackground( String[] params ) {
 
        if(params.length < 1){
 
            return false;
 
        }
 
        String arn = params[0];
 
        if(TextUtils.isEmpty(arn)){
 
            return false;
 
        }
 
        try {
 
            DeleteEndpointRequest request = new DeleteEndpointRequest();
 
            request.setEndpointArn(arn);
 
            AWSManager.getSNSClient().deleteEndpoint(request);
 
            return true;
 
        }catch(Exception ex){
 
            return false;
 
        }
 
    }
 
}

And now use the unregister task on your logout process.

You’re now ready to create your broadcast receivers, and your Android app will be ready to start receiving push notifications sent by AWS SNS.

Cheers!

We’ve gotten more than a few requests for the AWSManager that is referenced in the above example. Here it is

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.regions.Region;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.sns.AmazonSNSClient;
 
/**
 */
public class AWSManager {
 
    private static final String TAG = "mytag";
    public static final String _BUCKET = "mybucket";
    private static final String _ACCESS_KEY_ID = "1234567890";
    private static final String _SECRET_KEY = "1234567890";
 
    private static AmazonS3Client s3Client = null;
    private static AmazonSNSClient snsClient = null;
 
    /**
     * returns the instance of the amazon S3 Client for this app */
    public static AmazonS3Client getS3Client(){
        if(s3Client == null){
            s3Client = new AmazonS3Client( new BasicAWSCredentials( _ACCESS_KEY_ID, _SECRET_KEY ) );
        }
        return s3Client;
    }
 
    /**
     * returns the instance of the amazon SNS Client for this app */
    public static AmazonSNSClient getSNSClient(){
        if(snsClient == null){
            snsClient = new AmazonSNSClient( new BasicAWSCredentials( _ACCESS_KEY_ID, _SECRET_KEY ) );
            snsClient.setRegion(Region.getRegion(Regions.US_WEST_1));
        }
        return snsClient;
    }
 
}

Creating an Android Image Slider Control for your App (Part 2 of 3)

POSTED ON September 23rd  - POSTED IN Android

In Part 1 of of Creating an Android Image Slider Control, we built out the ImageSlider class, but when you added this class to a View, the horizontal scrolling didn’t behave well. In order to resolve the scrolling issue, we started playing with the Gesture options and listeners. We tried implementing the onFling event, but we did not receive consistent behavior on my older Samsung device. As a result we opted for handling the TouchEvent in our own code,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
@Override
    public boolean onTouchEvent(MotionEvent ev) {
        super.onTouchEvent( ev );
    	final int action = MotionEventCompat.getActionMasked(ev); 
 
        switch (action) { 
 
	        case MotionEvent.ACTION_DOWN: {
	            final int pointerIndex = MotionEventCompat.getActionIndex(ev); 
 
	            // Remember where we started (for dragging)
	            touchX = MotionEventCompat.getX(ev, pointerIndex);
	            touchY = MotionEventCompat.getY(ev, pointerIndex);
	            distanceX = 0;
	            distanceY = 0;
 
	            // Save the ID of this pointer (for dragging)
	            mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
	            break;
	        }
 
	        case MotionEvent.ACTION_MOVE: {
	            // Find the index of the active pointer and fetch its position
	            final int pointerIndex = 
	                    MotionEventCompat.findPointerIndex(ev, mActivePointerId);  
 
	            final float x = MotionEventCompat.getX(ev, pointerIndex);
	            final float y = MotionEventCompat.getY(ev, pointerIndex);
 
	            // Calculate the distance moved
	            distanceX += x - touchX;
	            distanceY += y - touchY;
 
	            invalidate();
 
	            // Remember this touch position for the next move event
	            touchX = x;
	            touchY = y;
 
	            break;
	        }
 
	        case MotionEvent.ACTION_CANCEL: {
	        	final int pointerIndex = MotionEventCompat.getActionIndex(ev); 
	            final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex); 
 
	            if (pointerId == mActivePointerId) {
	            	mActivePointerId = INVALID_POINTER_ID;
 
	            	//i need to know if the movement in X was bigger than the movement in Y.
	            	if( Math.abs(distanceX) > Math.abs(distanceY) ){
		            	if(distanceX > 0){
		            		onSwipeRight();
		            	}else if(distanceX < 0){
		            		onSwipeLeft();
		            	}
	            	}
	            }
	            break;
	        }
 
        }       
        return true;
    }
 
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        // Do not allow touch events.
        return false;
    }
 
    public void onSwipeRight(){
 
    }
 
    public void onSwipeLeft(){
 
    }

Handling the onTouch and onInterceptTouch events cancels the scrolling behavior of the component and allows us to add a new behavior that will scroll item by item. We implement our own scrolling logic in a moveToSlide function.

pub

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public void moveToSlide(int index){
	//let's ignore movement to images outside the bounds of the slider
    	if(index < 0 || index >= images.size()){
    		return;
    	}
    	this.currentSelectedIndex = index;
    	int childX = Math.round( layout.getChildAt(index).getX() );
    	this.smoothScrollTo(childX, 0);
}
 
public int getCurrentSlide(){
    	return this.currentSelectedIndex;
}
 
public void onSwipeRight(){
    	moveToSlide( getCurrentSlide() - 1);
}
 
public void onSwipeLeft(){
    	moveToSlide( getCurrentSlide() + 1);
}

In our next post, we’ll resolve the scenarios where the ImageViews are wider than the actual screen size, and complete the Image Carousel.

Creating an Android Image Slider Control for your App (Part 1 of 3)

POSTED ON September 18th  - POSTED IN Android

Would you like to create an Android Image Slider Control for your App?

At MobileAWS, we needed to build an Image Slider Control into one of our client’s Android apps. Initially, we thought it would be as simple as implementing the standard Android Gallery Widget,

http://developer.android.com/reference/android/widget/Gallery.html

Unfortunately, the Gallery Control has been deprecated, which means that it won’t work on new devices.

After digging around a little bit more, we found that most of the implementations were based upon the the ViewPager,

http://developer.android.com/reference/android/support/v4/view/ViewPager.html

The ViewPager has problems as well. First, it’s not supported on old devices. Second, since the the ViewPager is new, it stated that “”Note this class is currently under early design and development. The API will likely change in later updates of the compatibility library, requiring changes to the source code of apps when they are compiled against the newer version.” Yuck.

So at MobileAWS, we built our own Image Slider based upon the HorizontalScrollView. The first part was easy. We just created a HorizontalScrollView to hold a Horizontal Linear Layout with the slider items. The Option class is just a bean that has a name and a URL. The DownloadImageTask is an asynchronous task that will handle loading and caching the images on an ImageView.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class ImageSlider extends HorizontalScrollView {
	private List images = new LinkedList();
	private LinearLayout layout;
	private int currentSelectedIndex = 0;
 
	public ImageSlider(Context context) {
		super(context);
		init(context);
	}
 
	public ImageSlider(Context context, List images) {
		super(context);
		this.images = images;
		init(context);
	}
 
	void init(Context context) {
		setHorizontalFadingEdgeEnabled(false);
		setVerticalFadingEdgeEnabled(false);
 
		layout = new LinearLayout(context);
		LinearLayout.LayoutParams lParams = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
		layout.setLayoutParams(lParams);
		this.addView(layout);
 
		for(Option image: images){
			View sliderItem = ((Activity)context).getLayoutInflater().inflate(R.layout.slider_item, layout, false);
			ImageView tmpImage = (ImageView)sliderItem.findViewById(R.id.sliderImage);
			tmpImage.setContentDescription(image.getName());
 
			TextView tmpText = (TextView)sliderItem.findViewById(R.id.sliderText);
			tmpText.setText(image.getName());
 
			DownloadImageTask imageTask = new DownloadImageTask(tmpImage);
			imageTask.execute(image.getValue());
			layout.addView(sliderItem);
		}
	}
}

We also created the layout for each of the slider items as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical" >
 
    <ImageView 
        android:id="@+id/sliderImage"
        android:layout_width="wrap_content"
    	android:layout_height="wrap_content"
    	android:adjustViewBounds="true"
        android:scaleType="centerInside"
        android:padding="5dp"
        />
 
    <TextView 
        android:id="@+id/sliderText"
        android:layout_width="wrap_content"
    	android:layout_height="wrap_content"
    	android:layout_gravity="center_horizontal"
        />
 
</LinearLayout>

Our next post will detail some of the issues that we had in adding this ImageSlider to a view.

Facebook FBF8 2014 – Highlights Facebook Login, App Links, Parse, and more

POSTED ON May 6th  - POSTED IN Cloud

fbf8

We attended FBF8 2014 yesterday.

The highlights were

1. The Facebook Login mechanism is changing. What is the Facebook Login mechanism? When you have a Facebook App on your desktop browser in certain situations you will be prompted to login with Facebook at which point you will be prompted to give access to some of your information to a 3rd party app. Today, when the 3rd party app asks for permissions, the user has to either give all information or cancel out of the dialog. Starting yesterday, all new Facebook apps will make use of a new authentication mechanism that enables the end user to opt out of giving access to their requested data. For example, if a 3rd party app wants the user to give them their email and phone number, they will NOT have to. The user will still login to the app, but the app will not be able to acquire the data. All new apps will now have to check after a user logins to see if the end user provided the requested data. If the end user did NOT and it’s required, the 3rd party app will have to launch the login dialog again. In addition, Facebook introduced the Anonymous Login mode with a few select customers, e.g. Flipboard. Existing Apps will have 1 year to refactor their Facebook authentication.

2. App Links -Today, when users go into a mobile app there isn’t a good way to navigate into another mobile app. If I’m in the New York Times app and I want to get to the New York Times page on Facebook, I have to exit the New York Times App. Open the Facebook Mobile App. Search for New York Times. Click on the New York Times item. This is painful. App Links enable developers to embed direct shortcuts into the New York Times App enabling people to go directly to the Facebook App New York Times page.This is huge because it’s now building an anchor link url type experience on your phone. Mobile apps will hopefully become interconnected.

Facebook has open sourced the AppLink solution here, http://www.applinks.org. The syntax appears to be similar to the OG tags that many developers already use today.

3. Parse – Parse is a framework that FB acquired in 2013 for $87 mill, http://www.parse.com. Parse can be used to build Instagram type apps quickly. Parse provides a tutorial for an Instagram type app called AnyPic. Parse takes care of the backend logic enabling you to focus on the UI and Design. Parse has native code written for most mobile operating systems. It appears to give you Push Notifications, similar to Urban Airship, out of the box. It also enables you to leverage Facebook Insights directly. The data model is represented in JavaScript. The Mobile Apps correspond to an individual Facebook App on the developers.facebook.com platform.

4. Facebook Audience Network is similar to Twitter’s MoPub and Google’s AdMob. You can now build Ads into your third party apps leveraging FB data targeting.

5. iOS Like button – Believe it or not but on iOS mobile apps there is no support for embedding a like button to your presence on Facebook. Facebook has now added support for this in their Facebook.framework.

6. Facebook support – For those of you have developed on the Facebook Platform, you have most likely felt great pain. Facebook is now promising to fix the major bugs within 48 hours. They’ve also agreed to not change the API for 2 years. I’m not quite sure how the changing of the API will work, but it will certainly be nice not to have the ground moving underneath you.

How to Debug Ruby on Rails through SSL with RubyMine Foreman

POSTED ON February 28th  - POSTED IN Cloud, Ruby, Security

If you’re required to debug Ruby on Rails in RubyMine Foreman through SSL, e.g. you’ve got a Facebook App, you will find the following useful.

Unfortunately, I’ve not been able to find a way to debug by using the standard debug rails configuration.  Instead, I had to take the following steps

1. Install Foreman. Foreman is a nice little gem that enables you to launch multiple processes through the use of a Procfile. You can find more info here, http://blog.daviddollar.org/2011/05/06/introducing-foreman.html. The easiest way to add Foreman is to add the Foreman gem to your Gemfile, and run bundle install.

gem ‘foreman’

2. After you install Foreman, you’ll need to figure out your Foreman path. I’m using RVM. My Foreman is installed here:

/Users/joelgarcia/.rvm/gems/ruby-1.9.3-p484/gems/foreman-0.63.0/bin/foreman

3. In RubyMine, go to RunEdit Configurations. The Run/Debug Configurations Dialogwill come up. Typically, when you debug in RubyMine, you’re debugging in Rails. We’re going to do something a little different, we’re going to debug using a Ruby Script. Click on the + button in the upper left-hand corner. Select Ruby. A new configuration dialog will be displayed. Take the following steps:

  • On the Config Tab, Name: Debug SSL
  • On the Config Tab, Ruby Script: Path to Foreman in my case: /Users/joelgarcia/.rvm/gems/ruby-1.9.3-p484/gems/foreman-0.63.0/bin/foreman
  • On the Config Tab, Script Argument: Start
  • On the Config Tab, Working Directory: Your Project’s root directory.
  • On the Bundler Tab, check the “Run the script in context of the bundle
  • On the Logs, add a new entry where the Log File Entry is equal to development log.

It should look something like this

RubyMine Debug Config

4. Now our configuration is done, but we need to create a Procfile for Foreman to know what to launch when we chose to debug the Debug SSL configuration. Navigate to the root of your Ruby project and create a textfile entitled ‘Procfile’. In the Procfile, you will want to put in the following:

web: bundle exec thin start –debug -p 3000 –ssl –ssl-verify –ssl-key-file /Users/path_to_key/selfsigned.key –ssl-cert-file /Users/path_to_key/selfsigned.crt

Now, what is this? In the Procfile, we’re going to start the Thin web server with the appropriate RVM version. Thin is going to listen on port 3000, and we’re going to run SSL through port 3000 using our SSL keys and cert.

5. Now, in RubyMine, go to RunDebug Debug SSL, and your debugger should launch. In my situation, I’m using a self signed certificate. On my Mac, I’ve only been able to navigate to my https://0.0.0.0:3000 using Firefox d. I’ll write up another article on creating a self signed certificate using OpenSSL later.

 

Dynamic Facebook Permissions for your Facebook App

POSTED ON January 26th  - POSTED IN Social

Let’s say you’ve written a Facebook App using the Facebook JavaScript API, and you don’t want to ask for a ton of permissions when the user signs up for your Facebook App because you’re afraid the permissions will scare them  away.

No worries.

What you can do is in JavaScript land ask for permissions when you need them.

How do you do this?

First, you’d define a handy dandy little JavaScript object to ask for permissions. I’m using ExtJs so I use Ext.define to define the object.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
Ext.define('Common.FacebookSecurity',
{
    statics:
    {
         /** All functions here assume facebook api is already loaded */
         validatePermissions: function(permissions,FBObject,callbackFunction,scope,returnOnly)
         {
            FBObject.api('/me/permissions',function(response)
            {
                var values = response.data[0];
                /** Some invocations may want to get the permissions results*/
                if(returnOnly)
                {
                    callbackFunction.call(scope,values);
                    return true;
                }
                var compPerms = "";
                for(var i = 0; i < permissions.length; i++)
                {
                    if(values[permissions[i]] != 1)
                    {
                        // if there's something already on the composite permissions, add "," before to separate.
                        compPerms = compPerms + (compPerms != "" ? "," : "") + permissions[i];
                    }
                }
                if(compPerms == "")
                {
                    callbackFunction.call(scope);
                    return true;
                }
                Common.FacebookSecurity.askPermission(compPerms,FBObject,callbackFunction,scope);
                return true;
            });
        },
        askPermission: function(permission,FBObject,callbackFunction,scope)
        {
            FBObject.ui(
            {
                method:"permissions.request",
                perms: permission
            },
            function(response)
            {
                if(response.perms == permission)
                {
                    callbackFunction.call(scope);
                }
                else
                {
                    Ext.Msg.alert("Warning! Some of the actions you selected cannot be performed without the permissions required by facebook");
                }
                return true;
            });
            return true;
        }
    }
});

After you define the FacebookSecurity object, you’re now able to ask for permissions on demand.

Let’s say that I want permission to the user_photos before I show the end user an album of their photos. I’d just wrap my code to show the album with the FacebookSecurity.validatePermissions call.

1
2
3
4
5
6
Common.FacebookSecurity.validatePermissions(['user_photos','publish_stream'],
    FB,function()
    {
        this.win.viewAlbums();
    }
 ,this);

If we don’t have permissions to see the user_photos or publish_stream, then we will invoke Facebook to prompt to grant access. If the user doesn’t grant access, then we don’t show the album.

Easy Enough?

Using JSBuilder to compress your Sencha Touch Apps

POSTED ON January 26th  - POSTED IN Mobile

If you’re writing any sort of Sencha app, you must aggregate and minify the JavaScript using the JSBuilder utility. Unfortunately, the JSBuilder utility is cryptic.

You can snag JSBuilder here.

Once you get it installed, you need to build a jsb3 file. Don’t try to build the jsb3 file on your own. The easiest way to create the jsb3 file is to use the following JSBuilder sencha command.

1
sencha create jsb -a http://localhost:8080/YourPath/index.html -p C:YourPathindex_app.jsb3 -v

This command will generate a jsb3 file in the YourPath directory by munging the contents of your index.html.

Open up the jsb3 file. It will be json. You should see all of your javascript files plus the required ExtJs ones. Some of the dependencies in ExtJs are screwed up so if you’re bold you can remove the ones that don’t make any sense to make your payload smaller, e.g. Svg.js.

Every time you want to build a minified version of your ExtJs app, you’ll then want to then invoke the following bit of JSBuilder code to use your jsb3 file as the equivalent of a makefile, and spit out some compressed and obfuscated JavaScript.

1
sencha build -p C:YourPathindex_app.jsb3 -d C:YourPath -v

This will spit out a lovely all-classes.js and app-all.js. You will then want to place the app-all.js in your production directory.

Selling

POSTED ON January 26th  - POSTED IN Sales

In doing startups, either you quickly get over the fear of writing emails or calling complete strangers and asking them to purchase your goods or you perish.

Even when you make a connection with a prospective buyer , many of these people will invariably say, “Yes, I’d like the product. I will install it today!” and, of course, they disappear.

This can be incredibly frustrating.

My strategy in dealing with these people is to

1. Give them a little bit of breathing room.

2. Try to get them back on the phone.

3. If you can’t get them on the phone, follow up with an email entitled, “Still Interested?” In this email, you’ll ask for whether there has been a change in priorities or if something else has come up. Most likely, this prospective customer isn’t going to purchase your product, but you’re effectively trying to get out of them why they’re not going to go with you so you can get some closure.

4. If they don’t respond to the “Still Interested” email, then go with the “Is this dead?” email. Again, you’re effectively trying to solicit some sort of response to get them to re-engage. This may strike you as being a counter intuitive, but all you’re doing is trying to get them to respond so you can understand what’s going on.

Happy Selling.

ECommerce Authorize.Net

POSTED ON January 26th  - POSTED IN ECommerce
Are you using Authorize.Net to process credit card subscriptions? Do you ever have failures in processing credit cards and not know what to do?
Here are some quick steps to take so you can continue to bill your clients when their credit cards fail. Unlike PayPal, when your clients’ credit cards fail you will NOT receive a Silent Post. Instead, you will receive an email stating that a transaction failed for a particular ARB Subscription. When you receive this email, you must first reach out to your client to obtain a credit card that works. After you obtain this credit card information, your clients will most likely not want to re-enter their credit card information into your Credit Card UI. You will want to process the transaction for them. You will want to take the updated credit card information obtained from your client, their subscription id and login to your Authorize.net account.After you login, you will have two goals
1. Update the failed subscription with new credit card information acquired from your client.
2. Do a one time Virtual Terminal Authorize and Capture to make up for the credit card transaction that just failed.

  1. After you’ve logged into your Authorize.net account, select Recurring Billing from the Tools Menu on the left hand side.
  2. Select the subscription that failed. On the ARB Subscription Detail, click on the Edit Subscription.
  3. Enter the new credit card information. Submit the information
  4. Return to https://account.authorize.net/
  5. Go to Virtual Terminal from the Tools Menu
  6. Do a Authorize and Capture on the new credit card.
  7. Congrats! You’ve now successfully updated your client’s credit card information so you can continue to collect money via their existing subscription.
Back to Top
Social media & sharing icons powered by UltimatelySocial
Facebook
Google+
Twitter