docker exec -it --user root {container_name/id} bash
Friday, September 11, 2020
Docker Command Quick List
Login container as root or a specific user:
Friday, April 22, 2016
Add Google Analytics to your iOS Apps
Google Analytics provides powerful information basing on the usage of your web application. It can also provide similar service to your mobile applications. Here's how you can do it:
Create mobile app GA account
Add Google Analytics to iOS App
1. Install Google Analytics using CocoaPods:
Create mobile app GA account
- In GA, go to Admin tab, click ACCOUNT drop down and select 'Create new account'.
- On New Account page, pick 'Mobile app'. Specify your account name, your app name, industry category etc.
- Click on 'Get Tracking ID'
- You can create multiple Tracking ID for the same mobile app by selecting 'Create new property' under 'PROPERTY' section
1. Install Google Analytics using CocoaPods:
- If you haven't use pod for our current iOS app, open a terminal and cd to your Xcode project of your app, run: pod init to create a Podfile
- Open Podfile, and add: pod 'Google/Analytics'
- Now install pod by running this: pod install
- As of now, it installs the following libraries:
- Google (1.3.2)
- GoogleAnalytics (3.14.0)
- GoogleInterchangeUtilities (1.1.0)
- GoogleNetworkingUtilities (1.0.0)
- GoogleSymbolUtilities (1.0.3)
- GoogleUtilities (1.1.0)
- On 'PROPERTY' section, select .js > Tracking Code
- Then 'Getting started guide' link next to 'Google Analytics iOS SDK
- It will take you to 'Analytics for iOS' page. Select which language you use for iOS development, for me I select Swift
- Then click on 'GET A CONFIGURATION FILE' button, it looks like this:
- It will bring you to this page. where you can:
- Specify your app name and iOS bundle ID
- Choose and configure services (check Analytics)
- Download generated GoogleService-Info.plist file
3. Update your iOS app to include GA and configure GGLContext
- Add GoogleService-Info.plist to your iOS app
- Add #Import "Google/Analytics.h" to your app's Bridging-Header.h
- Add GA related header files to your project
- Override AppDelegate's didFinishLaunchingWithOptions to configure GGLContext:
//Configure tracker from GoogleService-Info.plist.
var configureError:NSError?
GGLContext.sharedInstance().configureWithError(&configureError)
assert(configureError == nil, "Error configuring Google services: \(configureError)")
// Optional: configure GAI options.
let gai = GAI.sharedInstance()
gai.trackUncaughtExceptions = true // report uncaught exceptions
gai.logger.logLevel = GAILogLevel.Verbose // remove before app release
4. Add screen tracking
- In your major screen view controller, add tracking code to the end of its viewWillAppear method:
let tracker = GAI.sharedInstance().defaultTracker
tracker.set(kGAIScreenName, value: screenName)
let builder = GAIDictionaryBuilder.createScreenView()
tracker.send(builder.build() as [NSObject : AnyObject])
Verify tracking
- Run your iOS app, navigate to screens with GA tracking code
- In GA Real-Time, you should be able to see a screen view:)
Wednesday, March 23, 2016
Working with Microsoft Excel using C#
Library
To programmatically access MS Excel, you will need Microsoft Excel Extension library usually comes with Visual Studio: Microsoft.Office.Interop.Excel. I usually include it in header like this:
using Excel = Microsoft.Office.Interop.Excel;
Open an Excel file
You can code like this:
var app = new Excel.Application();
var book = app.Workbooks.Open(fileName, 0, true, 5, "", "", true, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "\t", false, false, 0, true, 1, 0);
Or simply like this:
var book = app.Workbooks.Open(@"c:\test\data.xlsx");
Access a sheet, Range and cell
To access a sheet, say the first sheet (please note sheet index is 1-based rather than 0-based):
var sheets = book.Worksheets as Excel.Sheets;
var sheet = (Excel.Worksheet)sheets.get_Item(1);
To reach to a cell, you will do it through excel object Range, like this:
var range = sheet.UsedRange;
Or for multiple cells:
var range = (Excel.Range) sheet.get_Range(sheet.Cells[1, 1], sheet.Cells[3,3]);
Or for single cell:
var range = (Excel.Range) sheet.Cells[1, 1];
You got it, they are all 1-based. To access a cell:
var range = sheet.UsedRange;
for (int i = 1; i <= range.Rows.Count; i++)
{
for(int j=1; j <= range.Columns.Count; j++)
var s = (string) (range.Cells[i, j] as Excel.Range).Value2;
// ProcessString(s);
}
Clean up
Make sure to clean up after use. Here's how and don't forget to add try catch around it.
book.Close(false, Type.Missing, Type.Missing);
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(book);
System.Runtime.InteropServices.Marshal.FinalReleaseComObject(app);
Wednesday, March 16, 2016
Setup CocoaPods for Swift project
Cocoa Pods provides a standard way to manage dependent libraries. It only takes a few steps to set it up in a Swift project:
- Assume you've already have a normal Xcode project
- Open terminal window, $ cd to your project directory
- Create a Podfile by runing $ pod init
- Edit the Podfile to add any liberties you want to install within certain target
- Make sure to uncomment use_frameworks if it's a Swift project
- Run $ pod install
- An xcworkspace project should be created, please use it to load your project moving forwards
Friday, March 11, 2016
Define Google Analytics Custom Dimension
Google Analytics' Custom Dimension feature provides a powerful way to combine custom data with Analytics data. Here are a few steps to do it:
Create a GA account
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-74922222-4', 'auto');
ga('send', 'pageview');
</script>
Create a GA account
- Go to Admin UI, click ACCOUNT dropdown, select 'Create new account
- Fill in account create form and click 'Get Tracking ID'
Create environment specify property
- Under PROPERTY section, select 'Create new property'
- Fill in New Property form. You can use App Name for specific environment, like Staging, Dev etc.
- Click 'Get Tracking ID' for specified environment
Sample code to track traffic:
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-74922222-4', 'auto');
ga('send', 'pageview');
</script>
Create Custom Dimension
- In Admin UI, click 'Custom Definitions' under PROPERTY section
- Select 'Custom Dimensions', '+New Custom Dimension' button
- Give a name and specify scope, check active and hit 'Create'
- Follow the example codes to collect custom data
Sample code using javascript:
var dimensionValue = 'SOME_DIMENSION_VALUE';
ga('set', 'dimension1', dimensionValue);
View Result
- In Report UI, select Audience>Custom>User Defined
- From 'Secondary dimension' dropdown, select Custom Dimensions and your dimension name
- You should be able to see traffics group by each value of your custom dimension.
Friday, March 4, 2016
Steps to upload iOS app to Apple Store
- In XCode project General tab, make necessary updates to Version and Build number
- Set Build Only Device to Generic iOS Device
- Run Product>Archive
- Upon successful build and archive, click 'Upload to App Store' button
- Chose proper team for the submission
- Click 'Upload' to send the app to Apple
- Wait... until you get upload completed confirmation.
Tuesday, February 23, 2016
Git commands: get older version, set user email
From time to time there may be a need to play with a snapshot of an early version of code. Some Git/GitHub UI tool can help to do this, but it also just takes a few lines of command to do so. I use Mac as an example:
Check out older snapshot:
- Find the last commit you want to go back to. Copy the commit Id. It usually looks like this: 9d4f59ecf2eef3b8a46c5ba4a47a4efc27bf57b4
- Open Terminal, change to the directory where you code base were checked out.
- Enter: git checkout {Commit Id}
- You may see message like this:
You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by performing another checkout.
Return to latest version of master branch:
- Enter: git checkout master
Setup user email for local Git repository:
- Enter: git config --global user.email "{user email}"
To verify user email:
- Enter: git config --global user.email
Check out older snapshot:
- Find the last commit you want to go back to. Copy the commit Id. It usually looks like this: 9d4f59ecf2eef3b8a46c5ba4a47a4efc27bf57b4
- Open Terminal, change to the directory where you code base were checked out.
- Enter: git checkout {Commit Id}
- You may see message like this:
You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by performing another checkout.
Return to latest version of master branch:
- Enter: git checkout master
Setup user email for local Git repository:
- Enter: git config --global user.email "{user email}"
To verify user email:
- Enter: git config --global user.email
Monday, February 23, 2015
How to generate report with data from different databases
From time to time we need to generate reports referencing
data from other database. In SSRS, One simple way is to utilize its Lookup function.
All you need to do is create multiple datasets pointing to different
databases (data sources). Then use Lookup function following this syntax:
Lookup (src_expression,
target_expression, target_result_expression, target_dataset)
For example, you have application user dataset, say AppUsers(Uid)
and membership dataset, say MembershipUsers(Mid, Name). To lookup application
user name from membership, the expression will be something like this:
Lookup(Field!Uid.Value,
Field!Mid.Value, Field!Name, “MembershipUsers”)
Notice, Lookup function is good for 1-to-1 source/target field
value match. To handle 1-to-many matches, use LookupSet instead.
Tuesday, November 4, 2014
Simple way to compress Web API Response
There are many ways to do Web API Response compression. I particularly like Radenko Zec's solution mostly because it's simple and efficient.
A few steps I did in my project:
- Pick your favorite compression tool. I used DotNetZip as Radenko recommended. It gives me close to 85% compression rate for JSON. A CompressionHelper is used as well so that I can easily change to a different compression tool if I want.
- Implement DeflateCompressionAttribute. Mostly Radenko's code:
{
public override void OnActionExecuted(HttpActionExecutedContext actContext)
{
var content = actContext.Response.Content;
var bytes = content == null ? null : content.ReadAsByteArrayAsync().Result;
var compressedContent = bytes == null ? new byte[0] : CompressionHelper.Compress(bytes);
actContext.Response.Content = new ByteArrayContent(compressedContent);
actContext.Response.Content.Headers.Remove("Content-Type");
actContext.Response.Content.Headers.Add("Content-encoding", "deflate");
actContext.Response.Content.Headers.Add("Content-Type", "application/json");
base.OnActionExecuted(actContext);
}
}
- Add [DeflateCompression] attribute to any WebAPI method needs compression
- Decompress using standard HttpClientHandler. As our Web API client is expecting objects, we configure HttpClientHandler to decompress response:
using (var client = new HttpClient(new HttpClientHandler(){AutomaticDecompression = DecompressionMethods.Deflate|DecompressionMethods.GZip}))
{
var para = new ServiceParams();
client.BaseAddress = new Uri(_serviceUrl);
client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("gzip"));
client.DefaultRequestHeaders.AcceptEncoding.Add(new StringWithQualityHeaderValue("deflate"));
HttpResponseMessage response =
client.PostAsync(_servicePath, para, new JsonMediaTypeFormatter()).Result;
r = response.Content.ReadAsAsync<ServiceResponse>().Result;
}
Monday, September 10, 2012
Give the Badmail messages a second chance
You don't do this everyday, so just post it here for the record.
Steps to resend bad messages:
- Stop the SMTP Service: Open IIS Manager, right-click SMTP Virtual Server, then click Stop.
- Copy all bad message files (with extension of .bad) under Badmail folder to Pickup folder
- Remove the .bad extension
- Start the SMTP service, and watch them being redelivered.
File types under Badmail folder:
- .bad: the message failed to send
- .bdp: the diagnostic message
- .bdr: the body of the (None-delivery report) NDR.
Steps to resend bad messages:
- Stop the SMTP Service: Open IIS Manager, right-click SMTP Virtual Server, then click Stop.
- Copy all bad message files (with extension of .bad) under Badmail folder to Pickup folder
- Remove the .bad extension
- Start the SMTP service, and watch them being redelivered.
File types under Badmail folder:
- .bad: the message failed to send
- .bdp: the diagnostic message
- .bdr: the body of the (None-delivery report) NDR.
Tuesday, July 10, 2012
Claim-Based Authorization for ASP.NET MVC
As WIF becoming an official part of .NET 4.5, more attentions have been paid to integrate it with other .NET technologies, like MVC or Azure. Many consider WIF as a Claim-based authentication standard, actually it also comes with a well-rounded Claim-based user authorization mechanism. In this post, I'll discuss the following topics to show how easy to apply Claim-based authentication to ASP.NET MVC, and how powerfully and flexible it can be compare to standard Role-based authentication:
WIF provides a claims authorization manager for 'an extensibility point from which you can authorize access to a resource based on the claims presented in a token, before your RP application is called (from msdn)'. I personally like it because the following reasons:
To use custom authorization manager, just define it in claimsAuthorizationManager section in web.config. Something like this:
Please note, configuring claim authorization manager policies is another way to define your access control logic. It's good to use in a way that it requires less code changes when access logic changed. But it's relatively rigid, so not my favorite.
Apply to ASP.NET MVC
Now you can apply ClaimsPrincipalPermission attribute to any Controller Actions you plan to do access control. Just make sure to provide correct resource(s) and operation as parameters, because they will be used by claim authorization manager to make access control decision.
Similar authorization control can also be used for AJAX Web Services. In this case, simply apply ClaimsPrincipalPermission Attribute to Web Method. Make sure AJAX client to handle the security exception once an access attempt is denied.
- Claims Authorization Manager
- Apply to ASP.NET MVC
WIF provides a claims authorization manager for 'an extensibility point from which you can authorize access to a resource based on the claims presented in a token, before your RP application is called (from msdn)'. I personally like it because the following reasons:
- It makes more factors available for consideration as to doing access control logic, like Resources, Operations, Principle Claims(including Roles), all of which enable more detailed access control than regular Role-based access control.
- When used with .NET Attributes, it provides a clean separation between the code to implement a feature and the code to implement access control. See sample code below.
public class MyClaimAuthorizationManager : ClaimsAuthorizationManager
{
public override bool CheckAccess(AuthorizationContext context)
{
var claimsId = context.Principal.Identity as IClaimsIdentity;
if(claimsId==null || !claimsId.IsAuthenticated)
{
return false;
}
var resource = context.Resource.First().Value;
var operation = context.Action.First().Value;
{
public override bool CheckAccess(AuthorizationContext context)
{
var claimsId = context.Principal.Identity as IClaimsIdentity;
if(claimsId==null || !claimsId.IsAuthenticated)
{
return false;
}
var resource = context.Resource.First().Value;
var operation = context.Action.First().Value;
// Do your access control logic here
// ...
return false;
}
}
To use custom authorization manager, just define it in claimsAuthorizationManager section in web.config. Something like this:
<microsoft.identityModel>
<service>
<claimsAuthorizationManager type="MyNameSpace.MyClaimAuthorizationManager">
<service>
<claimsAuthorizationManager type="MyNameSpace.MyClaimAuthorizationManager">
<policy resource="http://localhost/MyService.svc" action="GET">
<claim claimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/dateofbirth" minAge="21" />
</policy>
<claim claimType="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/dateofbirth" minAge="21" />
</policy>
</claimsAuthorizationManager>
...
</service>
</service>
</ microsoft.identityModel>
Please note, configuring claim authorization manager policies is another way to define your access control logic. It's good to use in a way that it requires less code changes when access logic changed. But it's relatively rigid, so not my favorite.
Apply to ASP.NET MVC
Two things need to be handled before you can fully enjoy the claim-based access control for MVC. First, make sure your controller responsive to System.Security.SecurityException. This can be achieved by applying HandleError Attribute to controller, so MVC knows which view to go in the case of an access attempt is denied. Of course, the same attribute can be applied at Controller Action level for more fine-grained access-deny handling. Secondly, make sure your application supports custom Error handling. This can be achieved by setting customError mode to ‘On’ in
web.config.
<system.web>
<customErrors
mode="On"
defaultRedirect="Home/Logout"></customErrors>
…
</system.web>
Now you can apply ClaimsPrincipalPermission attribute to any Controller Actions you plan to do access control. Just make sure to provide correct resource(s) and operation as parameters, because they will be used by claim authorization manager to make access control decision.
[HandleError(ExceptionType = typeof(SecurityException), View = "Home/Login")]
public class MyController : Controller
{
[ClaimsPrincipalPermission(SecurityAction.Demand, Resource = MyResources.REPORTS_ACCESS, Operation = MyOperations.GET)]
public ActionResult ViewReport()
{
public class MyController : Controller
{
[ClaimsPrincipalPermission(SecurityAction.Demand, Resource = MyResources.REPORTS_ACCESS, Operation = MyOperations.GET)]
public ActionResult ViewReport()
{
...
return View();
}
...
}
return View();
}
...
}
Similar authorization control can also be used for AJAX Web Services. In this case, simply apply ClaimsPrincipalPermission Attribute to Web Method. Make sure AJAX client to handle the security exception once an access attempt is denied.
Labels:
.Net,
Authorization,
C#,
Claim-based,
MVC,
WIF
Sunday, March 11, 2012
God Mode for Windows 7
Windows 7 has a hidden feature called 'God Mode'. It actually a simple folder which contains links for most of the windows settings. So where is it then? The answer may surprise you: anywhere. Simply create a new folder from any place you want, and name it as:
GodMode.{ED7BA470-8E54-465E-825C-99712043E01C}
That's it! Enjoy:)
GodMode.{ED7BA470-8E54-465E-825C-99712043E01C}
That's it! Enjoy:)
Friday, January 13, 2012
Good Regex Tutorial and Tool
and Expresso 3.0 http://www.ultrapico.com/Expresso.htm
Wednesday, March 30, 2011
Open command prompt shortcut for Windows 7
Windows 7 has a built-in feature allows user to open command prompt from window explorer. It's really simple, just hold SHIFT and right click any folder you'd like to open, and select 'Open command window here' from the pop-up menu.
Friday, January 14, 2011
IIS URL Rewrite
Rewriting URL for IIS could be tricky. With the right tool, not any more. IIS URL Rewrite 2.0 module provides a friendly UI for web administrators to easily setup rules to define URL rewriting behavior. The tool can be downloaded from here. After installation, the URL Rewrite module will show up under web site IIS section.
From here, select Add Rule(s) action, and you can define rewrite rule using Rule Edit Template. For example, I want to rewrite URL from ~/promotion to ~/productionOrder.asxp?promotion=1, I can define the rule this way:
Apply the rule, you will find the web.config has been updated with the following settings:
<system.webServer>
...
<rewrite>
<rules>
<rule name="Redirect rule 1 for guestpass" stopProcessing="true">
<match url="^promotion$" />
<action type="Redirect" url="ProductionOrder.aspx?promotion=1" />
</rule>
</rules>
</rewrite>
</system.webServer>
<rewrite>
<rules>
<rule name="Redirect rule 1 for guestpass" stopProcessing="true">
<match url="^promotion$" />
<action type="Redirect" url="ProductionOrder.aspx?promotion=1" />
</rule>
</rules>
</rewrite>
</system.webServer>
Restart web site, the rule should take effect now.
Subscribe to:
Posts (Atom)
