Tutorials

Tutorials about HTML, CSS, PHP, Javascript, and Photoshop

  • Home
    Home This is where you can find all the blog posts throughout the site.
  • Categories
    Categories Displays a list of categories from this blog.
  • Tags
    Tags Displays a list of tags that has been used in the blog.
  • Archives
    Archives Contains a list of blog posts that were created previously.
  • Login
3799

Ever wonder how performances of your songs on radio, clubs, or arenas magically turn into quarterly checks made out to none other than you, jukebox,  television

The primary role of performing rights organizations (PROs) is to track and collect performance royalties from those sources that make commercial use of the music of their contracted members. Composers, They deduct certain administrative costs and disperse the remaining royalties to these members which are comprised of songwriters, and publishers

Who Should Join and When?

Typically, most non-performing songwriters wait to join a PRO until they are either signed to a publishing deal or have a composition being commercially exploited. I joined sooner than I needed to because the concept sounded 'professional' to me at the time. A better decision is to educate yourself about which PRO meets your needs most effectively and make the decision from an educated vantage point. Do as I say, not as I did. In hindsight, I have been quite happy with my decision

Performing songwriters on the other hand should resist the notion that they aren't yet a big enough act to join. In fact, bring in some additional income via performance royalties,  It is quite possible that the performance venues hosting them could. Small change has a way of adding up

Uncovered Performances

Dramatic performances likes those on Broadway in the United States are not covered by the services of PROs. Which involve the sales of physical product such as CDs are also not licensed by PROs, Mechanical rights. Master rights, internet radio, grand rights, synch rights, and digital cable music are additionally not licensed by these organizations at this time.  

Which One Should I Join?

And SESAC, In the United States there are there PROs: BMI, ASCAP. A person cannot belong to more than one at any given time and there are specific windows of time as to when changes in PRO can take place. These are stated in your contract

BMI

http://www.bmi.com/royalties

No fees are required of composers and songwriters, In this PRO. The term of a contract with BMI is two years for songwriters and five years for publishers. Contracts automatically renew unless membership is terminated within your specific window or time

BMI relies on  yearly three-day monitoring sessions provided by commercial radio licensees. Each day of the yea is covered by different stations. The information is then compiled to determine a correct projection of what is trending across the country. This information is further fortified by pattern recognition technology

ASCAP

 http://www.ascap.com/members/payment/royalties

This PRO requires a one time joining fee of $35 but there are no additional annual dues. ASCAP bases its radio payments on an exact airplay accounting from MediaGuide as well as on data from a sample survey. A contract with ASCAP is on a year by year basis and is self-renewing if not terminated. They  provide a great, easy-to-understand royalty collection graphic on their website

SESAC

http://www.sesac.com/WritersPublishers/HowWePay/generalInfo

No joining fees are required to join SESAC but this smaller PRO is a bit more selective and usually requires at least an interview. They claim to be more personal and user friendly because of their smaller size. A contract last three years and termination requires sufficient notice. SESAC employs pattern recognition technology on over 1600 AM and FM stations but only pays for feature performances of a song which means that the performance is intended to be the listener's primary focus

Random Facts

Each PRO allows for bonuses achieved by reaching certain airplay numbers

TV and film production companies fill out cue sheets that detail the type of use of a composition and its duration which then determines the payable royalty

And reciprocal agreements exist between the different geographic locations, Most countries have a performing rights organization

Writers' royalties are paid to the writer unless the writer is contracted to a publisher in which case a pre-agreed upon split determines the payment share

In choosing your affiliation, many issues come into play which you may not be aware of in the early stages of your career. A television theme that I co-wrote a decade ago has netted me far more money than my co-writer simply because my PRO pays for opening AND closing credits while hers does not. Neither of us had a clue about this difference, but we certainly do now. What a costly lesson this has been for one of us

Personal relationships with your PRO staff can come into play too. A rep that you happen to click with or who admires your work can be very instrumental is setting up publishing meetings and even co-writes with other writer members of that PRO. Take your time and weigh the pros and cons of each agency before finding your home base. Good luck

Continue reading
0
6551
Final product image
What You'll Be Creating

Or include features that most of us never use, A lot of the popular weather apps on Google Play are either full of ads, require too many permissions. Wouldn't it be great if you could build your own weather app from scratch

In this tutorial,  I'm going to show you how. Showing the user exactly what they need to know about the current weather conditions, Our app will have a simple and minimalist user interface. Let's get started

1. Prerequisites

Before you continue, double-check that you have the following set up:

  • Eclipse ADT Bundle: You can download it at the Android Developer website
  • OpenWeatherMap API Key But it's free, : This isn't required to complete the tutorial. You can obtain one by signing up at theOpenWeatherMap website
  • Icons: I recommend you download the weather icons font created byErik FlowersYou need to download theTTFBecause we'll be using it in a native app, file. We'll use the font to render various icons depending on the weather conditions

2. Create a New Project

I'm going to call this appSimpleWeatherBut feel free to give it any name you like. Set the minimum required SDK to, Enter a unique package nameAndroid 2. 2And set the target SDK toAndroid 4. 4You can leave the theme at Holo Dark

Create a new Android Application

This app will only have one Activity and it will be based on the Blank Activity Template as shown below

Create a new activity

Name theActivity WeatherActivityWe'll be using aFragmentInside thisActivityThe layout associated with theActivityIsActivity_weather. XmlThe layout associated with theFragmentIsFragment_weather. Xml

The Activity details screen

3. Add the Custom Font

CopyWeathericons-regular-webfont. TtfTo your project'sAssets/fontsDirectory and rename it toWeather. Ttf

4. Edit the Manifest

The only permission this app needs isAndroid. Permission. INTERNET

<uses-permission android:name="android.permission.INTERNET"/>

We're only going to support, To keep this tutorial simplePortraitMode. The Activity node of the manifest should look like this:

<activity
    android:name="ah.hathi.simpleweather.WeatherActivity"
    android:label="@string/app_name"
    android:screenOrientation="portrait"
    >
    <intent-filter>
      <action android:name="android.intent.action.MAIN" />
      <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

5. Edit the Activity's Layout

There isn't much to change inActivity_weather. Xml It should already have aFrameLayoutAdd an extra property to change the color of theBackground to#FF0099CC

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="ah.hathi.simpleweather.WeatherActivity"
    tools:ignore="MergeRootFrame"
    android:background="#FF0099CC" />

6. Edit the Fragment's Layout

EditFragment_weather. Xml by adding five TextViewTags to show the following information:

  • City and country
  • Current temperature
  • An icon showing the current weather condition
  • A timestamp telling the user when the weather information was last updated
  • More detailed information about the current weather, such as description and humidity

Use aRelativeLayoutTo arrange the text views. You can adjust theTextSizeTo suit various devices

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="ah.hathi.simpleweather.WeatherActivity$PlaceholderFragment" >

    <TextView
        android:id="@+id/city_field"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <TextView
        android:id="@+id/updated_field"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/city_field"
        android:layout_centerHorizontal="true"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:textSize="13sp" />

    <TextView
        android:id="@+id/weather_icon"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"        
        android:layout_centerVertical="true"
        android:layout_centerHorizontal="true"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:textSize="70sp"
        />

    <TextView
        android:id="@+id/current_temperature_field"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:textSize="40sp" />

    <TextView 
        android:id="@+id/details_field"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/weather_icon"
        android:layout_centerHorizontal="true"
        android:textAppearance="?android:attr/textAppearanceMedium"
        />        

</RelativeLayout>

7. Edit strings.xml

This file contains the strings used in our app as well as the Unicode character codes that we'll use to render the weather icons. The application will be able to display eight different types of weather conditions. Then refer to, If you want to handle morethis cheat sheetAdd the following toValues/strings. Xml:

<. Xml version="1.0" encoding="utf-8". >
<resources>

    <string name="app_name">Simple Weather</string>
    <string name="change_city">Change city</string>

    <. -- Put your own APP ID here -->
    <string name="open_weather_maps_app_id">11111</string>
    
    <string name="weather_sunny">&#xf00d;</string>
    <string name="weather_clear_night">&#xf02e;</string>
    
    <string name="weather_foggy">&#xf014;</string>
    <string name="weather_cloudy">&#xf013;</string>
    <string name="weather_rainy">&#xf019;</string>
    <string name="weather_snowy">&#xf01b;</string>
    <string name="weather_thunder">&#xf01e;</string>
    <string name="weather_drizzle">&#xf01c;</string>
    
    <string name="place_not_found">Sorry, no weather data found. </string>    
    
</resources>

8. Add a Menu Item

The user should be able to choose the city whose weather they want to see. EditMenu/weather. XmlAnd add an item for this option

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context="ah.hathi.simpleweather.WeatherActivity" >

    <item
        android:id="@+id/change_city"
        android:orderInCategory="1"
        android:title="@string/change_city"
        app:showAsAction="never"/>

</menu>

Now that all the XML files are ready to use, let's move on and query the OpenWeatherMap API to fetch weather data

9. Fetch Data From OpenWeatherMap

We can get the current weather details of any city formatted as JSON using the OpenWeatherMap API. We pass the city's name and the metric system the results should be in, In the query string

 we send a request to , For example, to get the current weather information for Canberra, using the metric systemhttp://api.openweathermap.org/data/2.5/weather?q=Canberra&units=metric

The response we get back from the API looks like this:

{
    "base": "cmc stations", "clouds": {
        "all": 90
    }"cod": 200, "coord": {
        "lat": -35. 28, "lon": 149. 13
    }"temp_max": -1, "temp_min": -1, "pressure": 1023, "id": 2172517, "main": {
        "humidity": 100, "dt": 1404390600, "temp": -1
    }"name": "Canberra", "message": 0, "sys": {
        "country": "AU". "sunset": 1404370965, "sunrise": 1404335563, 313
    }"weather": [
        {
            "description": "overcast clouds", "id": 804, "main": "Clouds", "icon": "04n"
        }], "wind": {
        "deg": 305. 004, "speed": 1. 07
    }
}

Create a new Java class and name itRemoteFetch. JavaThis class is responsible for fetching the weather data from the OpenWeatherMap API

We use the HttpURLConnection class to make the remote request. The OpenWeatherMap API expects the API key in an HTTP header namedX-api-keyThis is specified in our request using theSetRequestPropertyMethod

We use aBufferedReaderTo read the API's response into aStringBufferWhen we have the complete response, we convert it to aJSONObject object

 the JSON data contains a field named , As you can see in the above responseCodIts value is200 if the request was successful. We use this value to check whether the JSON response has the current weather information or not

TheRemoteFetch. JavaClass should look like this:

Package ah. Hathi. Simpleweather;

import java. Io. BufferedReader;
import java. Io. InputStreamReader;
import java. Net. HttpURLConnection;
import java. Net. URL;

import org. Json. JSONObject;

import android. Content. Context;
import android. Util. String city){
		try {
			URL url = new URL(String, Log;

public class RemoteFetch {

    private static final String OPEN_WEATHER_MAP_API = 
			"http://api.openweathermap.org/data/2.5/weather?q=%s&units=metric";
	
	public static JSONObject getJSON(Context context. Format(OPEN_WEATHER_MAP_API, city));			
			HttpURLConnection connection = 
					(HttpURLConnection)url. OpenConnection();
			
			connection. Context, addRequestProperty("x-api-key". GetString(R. String. Open_weather_maps_app_id));
			
			BufferedReader reader = new BufferedReader(
					new InputStreamReader(connection. GetInputStream()));
			
			StringBuffer json = new StringBuffer(1024);
			String tmp="";
			while((tmp=reader. ReadLine()). =null)
				json. Append(tmp). Append("n");
			reader. Close();
			
			JSONObject data = new JSONObject(json. ToString());
			
			// This value will be 404 if the request was not
			// successful
			if(data. GetInt("cod"). = 200){
				return null;
			}Return data;
		}Catch(Exception e){
			return null;
		}
	}	
}

10. Store the City as a Preference

The user shouldn't have to specify the name of the city every time they want to use the app. The app should remember the last city the user was interested in. We do this by making use ofSharedPreferencesInstead of directly accessing these preferences from our, HoweverActivity it is better to create a separate class for this purpose,  class

Create a new Java class and name itCityPreference. JavaCreate two methods, To store and retrieve the name of the citySetCity andGetCityTheSharedPreferencesObject is initialized in the constructor. The CityPreference. Java class should look like this:

Package ah. Hathi. Simpleweather;

import android. App. Activity;
import android. Content. SharedPreferences;

public class CityPreference {
    
	SharedPreferences prefs;
	
	public CityPreference(Activity activity){
		prefs = activity. GetPreferences(Activity. MODE_PRIVATE);
	}// If the user has not chosen a city yet, return
    // Sydney as the default city
	String getCity(){
		return prefs. GetString("city", "Sydney, AU");        
	}Void setCity(String city){
		prefs. Edit(). PutString("city", city). Commit();
	}
	
}

11. Create the Fragment

Create a new Java class and name itWeatherFragment. JavaThis fragment usesFragment_weather. XmlAs its layout. Declare the fiveTextView objects and initialize them in theOnCreateView method. Declare a newTypefaceObject namedWeatherFontTheTypeFaceObject will point to the web font you downloaded and stored in theAssets/fontsFolder

We will be making use of a separateThreadTo asynchronously fetch data from the OpenWeatherMap API. We cannot update the user interface from such a background thread. We therefore need aHandlerObject, which we initialize in the constructor of the WeatherFragment class

Public class WeatherFragment extends Fragment {
    Typeface weatherFont;
	
	TextView cityField;
	TextView updatedField;
	TextView detailsField;
	TextView currentTemperatureField;
	TextView weatherIcon;
	
	Handler handler;

	public WeatherFragment(){	
		handler = new Handler();
	}ViewGroup container, Bundle savedInstanceState) {
        View rootView = inflater, @Override
    public View onCreateView(LayoutInflater inflater. Inflate(R. Layout. Fragment_weather, container, false);
        cityField = (TextView)rootView. FindViewById(R. Id. City_field);
        updatedField = (TextView)rootView. FindViewById(R. Id. Updated_field);
        detailsField = (TextView)rootView. FindViewById(R. Id. Details_field);
        currentTemperatureField = (TextView)rootView. FindViewById(R. Id. Current_temperature_field);
        weatherIcon = (TextView)rootView. FindViewById(R. Id. Weather_icon);
        
        weatherIcon. SetTypeface(weatherFont);
        return rootView; 
    }
}

Initialize the WeatherFont object by calling CreateFromAsset on the TypefaceClass. We also invoke the UpdateWeatherData method inOnCreate

@Override
public void onCreate(Bundle savedInstanceState) {
    super. OnCreate(savedInstanceState);  
	weatherFont = Typeface. CreateFromAsset(getActivity(). GetAssets(), "fonts/weather.ttf");    	
	updateWeatherData(new CityPreference(getActivity()). GetCity());
}

InUpdateWeatherDataWe start a new thread and call GetJSON on theRemoteFetchClass. If the value returned byGetJSON isNullWe display an error message to the user. We invoke the ,  If it isn'tRenderWeather method

Only the mainThreadIs allowed to update the user interface of an Android app. CallingToastOrRenderWeatherDirectly from the background thread would lead to a runtime error. That is why we call these methods using theHandler'sPostMethod

Private void updateWeatherData(final String city){
    new Thread(){
		public void run(){
			final JSONObject json = RemoteFetch. City);
			if(json == null){
				handler, getJSON(getActivity(). Post(new Runnable(){
					public void run(){
						Toast. GetActivity(), makeText(getActivity(). GetString(R. String. Place_not_found), Toast. LENGTH_LONG). Show(); 
					}
				});
			}Else {
				handler. Post(new Runnable(){
					public void run(){
						renderWeather(json);
					}
				});
			}    			
		}
	}Start();
}

TheRenderWeather method uses the JSON data to update theTextView objects. TheWeatherNode of the JSON response is an array of data. In this tutorial, we will only be using the first element of the array of weather data

Private void renderWeather(JSONObject json){
    try {
    	cityField. SetText(json. GetString("name"). ToUpperCase(Locale. US) + 
    			", " + 
    			json. GetJSONObject("sys").getString("country"));
    	
    	JSONObject details = json. GetJSONArray("weather"). GetJSONObject(0);
    	JSONObject main = json. GetJSONObject("main");
    	detailsField. SetText(
    			details. GetString("description"). ToUpperCase(Locale. US) +
    			"n" + "Humidity: " + main.getString("humidity") + "%" +
    			"n" + "Pressure: " + main.getString("pressure") + " hPa");
    	
    	currentTemperatureField. SetText(
    				String. Format("%.2f", main.getDouble("temp"))+ " ℃");

    	DateFormat df = DateFormat. GetDateTimeInstance();
    	String updatedOn = df. Format(new Date(json. GetLong("dt")*1000));
    	updatedField. SetText("Last update: " + updatedOn);

    	setWeatherIcon(details. Json, getInt("id"). Json, getJSONObject("sys").getLong("sunrise") * 1000. GetJSONObject("sys").getLong("sunset") * 1000);
    	
	}Catch(Exception e){
		Log. E("SimpleWeather", "One or more fields not found in the JSON data");
	}
}

At the end of theRenderWeather we invoke ,  methodSetWeatherIcon with theIdOf the current weather as well as the times of sunrise and sunset. Because the OpenWeatherMap API supports more weather conditions than we can support with the web font we're using, Setting the weather icon is a bit tricky. Which you can read more about on the, the weather ids follow a pattern, FortunatelyOpenWeatherMap website

This is how we map a weather id to an icon:

  • The weather codes in the 200 range are related to thunderstorms, which means we can use R. String. Weather_thunderFor these
  • The weather codes in the 300 range are related to drizzles and we use R. String. Weather_drizzleFor these
  • The weather codes in the 500 range signify rain and we use R. String. Weather_rain for them
  • And so on

Depending on the current time of the day and only if the weather is clear, We use the sunrise and sunset times to display the sun or the moon

Private void setWeatherIcon(int actualId, long sunset){
    int id = actualId / 100;
	String icon = "";
	if(actualId == 800){
		long currentTime = new Date(), long sunrise. GetTime();
		if(currentTime>=sunrise && currentTime<sunset) {
			icon = getActivity(). GetString(R. String. Weather_sunny);
		}Else {
			icon = getActivity(). GetString(R. String. Weather_clear_night);
		}
	}Else {
    	switch(id) {
    	case 2 : icon = getActivity(). GetString(R. String. Weather_thunder);
		 		 break;     	
    	case 3 : icon = getActivity(). GetString(R. String. Weather_drizzle);
		 		 break;    	
    	case 7 : icon = getActivity(). GetString(R. String. Weather_foggy);
    			 break;
    	case 8 : icon = getActivity(). GetString(R. String. Weather_cloudy);
		 		 break;
    	case 6 : icon = getActivity(). GetString(R. String. Weather_snowy);
		 		 break;
    	case 5 : icon = getActivity(). GetString(R. String. Weather_rainy);
    			 break;
    	}
	}WeatherIcon. SetText(icon);
}

Of course, you can handle more weather conditions by adding moreCase statements to theSwitchStatement of theSetWeatherIcon method

Finally, add aChangeCity method to the fragment to let the user update the current city. TheChangeCity method will only be called from the main Activity class

Public void changeCity(String city){
    updateWeatherData(city);
}

12. Edit the Activity

During the project's setup,  Eclipse populatedWeatherActivity. Java with some boilerplate code. Replace the default implementation of theOnCreateMethod with the one below in which we use the WeatherFragmentThe OnCreate method should look like this:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super. OnCreate(savedInstanceState);
    setContentView(R. Layout. Activity_weather);

    if (savedInstanceState == null) {
        getSupportFragmentManager(). BeginTransaction(). Add(R. Id. New WeatherFragment()), container. Commit();
    }
}

Edit the, NextOnOptionsItemSelected method and handle the only menu option we have. All you have to do here is invoke the ShowInputDialog method

In the ShowInputDialog method,  we useAlertDialog. BuilderTo create aDialog object that prompts the user to enter the name of a city. This information is passed on to theChangeCity method, which stores the name of the city using the CityPreference class and calls theFragment'sChangeCity method

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    if(item. GetItemId() == R. Id. Change_city){
		showInputDialog();
	}Return false;
}Private void showInputDialog(){
	AlertDialog. Builder builder = new AlertDialog. Builder(this);
	builder. SetTitle("Change city");
	final EditText input = new EditText(this);
	input. SetInputType(InputType. TYPE_CLASS_TEXT);
	builder. SetView(input);
	builder. SetPositiveButton("Go", new DialogInterface. Int which) {
			changeCity(input, OnClickListener() {
		@Override
		public void onClick(DialogInterface dialog. GetText(). ToString());
		}
	});
	builder. Show();
}Public void changeCity(String city){
	WeatherFragment wf = (WeatherFragment)getSupportFragmentManager(). FindFragmentById(R. Id. Container);
	wf. ChangeCity(city);
	new CityPreference(this). SetCity(city);
}

Your weather app is now ready. Build the project and deploy it to an Android device for testing

The app running on a tablet

Conclusion

You now have a fully functional weather application. Feel free to explore theOpenWeatherMap APITo further enhance your application. You may also want to make use of moreweather iconsBecause we are currently using only a small subset of them

Continue reading
0
4376

Sometimes the available product types just aren’t enough. But there’s just situations where it simply makes more sense to have a separate product type, Magento has the different types pretty well covered.  

For me, one of those situations was when I needed to create a custom product module that would handle generating a customized PDF based on the options of the product that was purchased

This is not a solution if you need to create a product with a specific attribute since Magento already provides these features, Additionally. You should find this guide handy for when (or if) you need to be able to identify a specific type of product anywhere in the system, Instead.  

Like when hooking observers into it, For example

Set Up the Module

This guide is going to assume you have some basic knowledge of the inner workings of a Magento module. Building a good module is a bit of an art on its own and it's the subject matter of a post all its own. We're going to take a look at how to set up the module for it, With that said

Now, on to the main point of discussion

Like everything in Magento, our work here too starts in the configuration files. We want to create a separate module for this because in Magento, we want separate modules for everything. Keeping things separate and modular in Magento keeps us happy. Robogento being my company and Printable the module name, I ended up with Robogento Printable

We have to tell Magento about it and define it, To add a product type. These are the applicable parts of theConfig. XmlYou will need to fill in the blanks yourself, of course:, file we’re going to need

<config>. <global>. <catalog>
      <product>
        <type>
          <printable translate="label" module="printable">
            <label>Printable Product</label>
            <model>printable/product_type</model>
            <price_model>printable/product_price</price_model>
            <is_qty>1</is_qty>
          </printable>
        </type>

There we go.  

My module is defined as “printable”, As you can see, the label is for the benefit of filtering in the grids that show the product in the backend and when creating a new product. The model and price model are where the fun begins. This can be pretty straight forward, Every product needs a type

Extending Magento

Extend the base system by creating the Type model. Watch the file path based on the class definition:

Class Robogento_Printable_Model_Product_Type
    extends Mage_Catalog_Model_Product_Type_Simple { }

And there we go.  

Suddenly our printable product is now a clone of the Simple Product Magento is already aware of. What’s actually happening here is that by extending from the Simple Type, we’re reusing all of the features that product type has. The added benefit is we can very simply override the existing features with our own if we’re so inclined

There was one other thing I showed you for ourConfig. XmlFile. The product type’s price model. Very much the same thing applies here:

Class Robogento_Printable_Model_Product_Price
    extends Mage_Catalog_Model_Product_Type_Price { }

We’re simply extending from Magento’s existing system again, As you can see. But the simple and virtual product types were among the better thought out parts of it and it’s always a good idea to reuse existing code when you can, Magento has flaws enough

Normally, but in the case of Magento, I'd say there's more to it, this is really all there is to it. We’ve now essentially created an alias of the Simple Product system and named it Printable.  

The printable product will use all of the features and definitions as they are set up for the simple products. You name it If you only need an invoice address, So two addresses, extend from the Virtual product instead, inventory settings. If you want to provide downloadable products, but with a slightly different set of features, extend from the Downloadable system

What to Keep in Mind

This is not to create a T-Shirt type in your new Magento store, Like I said at the start. You’re most probably looking for the attribute sets in the backend, In that case

You'll be able to quickly filter out specific products, be it in the backend or when using other Magento features like observers or collections, Using this

Continue reading
0
12525

In this tutorial, we'll take a look atWebDriverJs which is a tool used for browser automation. Chrome will be used throughout however modern browsers tend to have available drivers for use with WebDriver (even mobile) so do check them out if you wish to automate other browsers

While unit tests are certainly valuable for modern web applications, at some point, you'll find bugs crop up which weren't caught by a unit test but would have theoretically been caught by an integration/acceptance test, as your application grows.  

Should you wish to follow a testing strategy which involves browser testing, this guide will give you an initial introduction to testing with WebDriverJs so you're equipped with enough knowledge to get started

This tutorial assumes you're familiar with JavaScript and can run JavaScript code usingnode.js

WebDriverJS

Feel free to checkout this, If you'd like to follow alongsample projectWhich contains a few WebDriver examples for you to run. You'll also need to installChromedriverAnd have it available in your path

Selenium WebDriver typically has a server and a client. Apart from WebDriver contributors, most people will only be interested in the client API which allows them to control a browser through their script. Install the JavaScript bindings for WebDriver:, To get started

Npm install selenium-webdriver

Once you've installed that module via NPM, you can require the module in your node code like this:

Require('selenium-webdriver');

Alternatively, if you check out the sample project, you can simply run anNpm installInside the folder as the WebDriverJs module is listed as a dependancy in thePackage. JsonFile

My personal favourite is the source itself, While you can browse the official documentation. Thiswebdriver.jsE, file lists many WebDriver methods. G. You'll notice agetAttributeAnd agetTextHere are some methods which may be of interest:

  • Get- Navigate the browser to a URL
  • FindElements- Similar toDocument. QuerySelectorAllIn the browser
  • ExecuteScript- Execute raw JavaScript onto the current page
  • GetText- Get the text content of an element including its children
  • IsDisplayed- Find out if an element is displayed on the page

Promises

One factor about the JavaScript bindings for WebDriver in particular is that almost every method is asynchronous. It means the following code doesn't actually get the title of the web page:

Var title = browser. GetTitle();
//logs { then: [Function: then],  cancel: [Function: cancel], isPending: [Function: isPending] }Console. Log(title);

Instead what you need to do, is this:

Var promise = browser. GetTitle();

promise. Then(function(title) {
    console. Log(title);
});

This is because WebDriverJs uses promises in order to make dealing with async code a bit more pleasant. Note that the promise implementation as part of WebDriverJs does not conform exactly to thePromises/A+Standard

The key thing to take away here is that most WebDriver methods will return aThenMethod which accepts two optional (function) arguments. The first argument is a callback which may receive a value.  

In the above example, therefore our callback will receive that title as its first argument, we asked for a title. The second optional function argument we can pass to the then method allows us to catch errors, if at all

Examples

Let's recap on where we are so far:

  1. We installed the Chromedriver binary
  2. We installed WebDriverJs via NPM
  3. With the understanding that almost everything is async, we know how to use promises to retrieve the values we want

Have a look at this code example:

Var webdriver = require('selenium-webdriver');
var browser = new webdriver.Builder().usingServer().withCapabilities({'browserName': 'chrome' }). Build();

browser. Get('http://en. Wikipedia. Org/wiki/Wiki');
browser. FindElements(webdriver. By. Css('[href^="/wiki/"]')). Then(function(links){
    console. Log('Found', links. Length, 'Wiki links. ' )
    browser. Quit();
});

Run theWiki example like this:

$ node Wiki. Js
Found 367 Wiki links

The first few lines are essentially boilerplate, In the code example. Like what browser to actually use, It initialises the browser object and specifies some initial configuration. Starting with the call toBrowser. GetWe have the code we really care about

  1. First we navigate to a Wikipedia page
  2. We construct a CSS selector which matches elements that have an attribute of href and a value starting with /wiki/ (e. G. Internal Wiki links)
  3. We pass the CSS selector into the, Still on the same line as step #2FindElementsMethod which will go ahead and asynchronously evaluate the selector expression
  4. To observe updates to the Promise, we pass a callback function to the then method
  5. The first argument to the callback is an array of matched elements, so we retrieve that and log the length
  6. We quit the browser, Finally

Let's take a look at another example which demonstrates carrying out a Google search and clicking on the result we expect to be on the page, Finding elements on the page is one piece of the puzzle

/*
* Carry out a Google Search
*/

"use strict";

var webdriver = require('selenium-webdriver');
var browser = new webdriver.Builder().usingServer().withCapabilities({'browserName': 'chrome' }). Build();

function logTitle() {
    browser. GetTitle(). Then(function(title) {
        console. Log('Current Page Title: ' + title);
    });
}Function clickLink(link) {
    link. Click();
}Function handleFailure(err) {
    console. Error('Something went wrong\n', err. '\n');
    closeBrowser();, stack
}Function findTutsPlusLink() {
    return browser. FindElements(webdriver. By. Css('[href="http://code.tutsplus.com/"]')). Then(function(result) {
        return result[0];
    });
}Function closeBrowser() {
    browser. Quit();
}Browser. Get('https://www. Google. Com');
browser. FindElement(webdriver. By. Name('q')). SendKeys('tuts+ code');
browser. FindElement(webdriver. By. Name('btnG')). Click();
browser. 2000), wait(findTutsPlusLink. Then(clickLink). Then(logTitle). HandleFailure);, then(closeBrowser

Running theabove code:

$ node GoogleSearch. Js
Current Page Title: Tuts+ Code Tutorials

A few interesting snippets are shown here. We can get a feel for what it's like to use function declarations - instead of anonymous function callbacks (that are passed to, FirstThenThe result is something like a, )fluent API(see the last line). Also, since we have the ability to create custom promises (deferreds), we can be as fluent as we desire.  

Note that we attach an error callback in the last call toThenEven if an error occurs earlier on, it'll still propagate up

We navigate to the Google home page and search for 'tuts+ code'. WebDriver's internal Control Flow mechanism knows to schedule each command to happen one after the other, this saves us the hassle of having to chain everything together and also explains why there are two calls to, Since we're operating on the browser objectFindElementWithout having to be chained to each other, one after the other

Waiting

No page reload occurs, therefore WebDriver will immediately try and find the elements we've instructed it to on the search results page, When we carry out the Google search from the homepage. Knowing when to wait for elements is a critical part of browser automation.  

The old and naughty way of doing things was to use aSleepSince the point at which an element appears can heavily depend on external factors (e. G. Developers can sometimes instruct WebDriver to wait for a fixed period of time before continuing, network connection speed). Of course, is riddled with problems, This.  

Fortunately, theWaitMethod makes automating modern web pages a lot nicer. You call wait with two arguments, the first is a function which needs to evaluate to true by a time period defined as the second argument to wait. WebDriver regularly calls your callback until either it returns true, or time has run out in which case an error is thrown

Modifying the Browser

While there are many methods to call on the context of DOM elements, you can also call methods on the browser itself to give you more control over the browser state. Here are a few simple examples to give you a better idea:

Set the dimensions of the browser window

Browser. Manage(). Window(). 720), setSize(1280

Connect the browser to a proxy:

Var proxy = require('selenium-webdriver/proxy');
browser = new webdriver. Builder(). UsingServer()
.withCapabilities({'browserName': 'chrome' }). SetProxy(proxy. Manual({
    http: '127. 1:9000'
})). Build();

And more, take a screenshot of the window, set some individual browser settings, You can also read, write, and delete cookies

Alternative Options

There are a number of options available when wanting to control a browser programatically. First of all, we took a look at the JavaScript bindings for WebDriver however there are some others:

E, WebDriverJs. G. The version we installed usingNpm install selenium-webdriverIs just one version of a WebDriver Client API written in JavaScript. There are also other options:, If you're keen to programatically control browsers via JavaScript

  • WD.js- Fluent API using promises + chaining
  • Leadfoot- Now used by the latest version of Intern
  • WebDriver.io- Has a bunch of documentation for use with BDD/TDD frameworks
  • Testium- Has cleardocumentationOn exactly what is supported
  • DalekJS- A fun looking website with pretty feedback when executing tests. A lot of DalekJS has been split out into modules which can be found onGithub
  • Nightwatch- Another tool with pretty looking feedback and a fluent API
  • Webdriver-sync- Synchronous version of interacting with WebDriver

Using something like WD. Js or Nightwatch can mean a number of things:

  • Different API to interact with. If the official JavaScript selenium-webdriver bindings has an API you're not used to, check out the alternative options above
  • Alternative feedback - this can be on the reporter level, but also simply what you see in the terminal after a test has failed locally

Conclusion

If you wish to start using WebDriver for the sake of testing, then that's great. How about just automating a repetitive task, You should also keep in mind that browser automation doesn't have to stop at testing.  

Check out this article on , For exampleGetting to Philosophy it essentially explains how continuously clicking on the first link in Wiki articles will eventually land you on the Philosophy article.  

This makes for a fun task to automate. Checkout out thisanimated gif or thesource to see it in action.  

Continue reading
0
5397

Remember when web designers used to be total nerds?

Back in the early 2000s, web design was a technical skill; just getting a site to function was considered an achievement in and of itself, and things like interface design, usability, and UX largely fell by the wayside.

But times have changed. In the past few years, something magical has happened: Web designers have stopped being just nerds, and have started becoming artisans.

We don’t talk about tech specs much anymore, we talk about "handcrafted code" and "beautiful UIs". We describe ourselves as "code poets" and "design ninjas", and it’s perfectly acceptable to many of our clients.

It’s gotten to the point where we sound a lot more like old-world craftsmen than web nerds, and let me tell you, it’s a wonderful feeling.

But sometimes I wonder: Is our newfound sense of artisanship really helping our clients?

After all, we sell products to businesses that are focused on profitability, not necessarily artistry.

In my experience, acting in this manner is bad for clients and for us web designers. When you try to sell a website as if it were a handcrafted artisanal item, a few things happen immediately.

You create a disconnect between what your clients need, and what you plan to provide

Some business owners care about design. All business owners care about profit.

We may think of our work as pieces of art, but to most business owners, their websites exist as functional tools for achieving certain goals (bring in new customers, drive up company profits, increase brand awareness, etc.).

So when we try to position our products as if they were handcrafted artworks, this creates a fundamental disconnect between what our clients need, and what we will actually provide.

This might not seem like much, but having the ability to understand your clients’ goals is the most crucial part of landing new projects. And when they’re talking about the bottom line, and you’re talking about beauty, your clients can easily start feeling confused.

You lose out on your most profitable clients

When you focus on artistry, you gain clients that appreciate artistry, but you also manage to lose out on the holy grail of clients in our industry: Medium-sized businesses. You’ve probably encountered a few of them. They’re generally mid-stage companies who are profit-focused, need quite a bit of web work done, and are more than happy to keep coming back to you with project after project over the course of several years. Why? Quite simply because they’ve found that your services are making them more money.

When you’re able to provide services centered on achieving your clients’ business objectives, they’ll naturally be compelled to keep working with you.

You limit the types of services you can provide

Artisan web designers and developers are generally pigeonholed into being design- or code-focused, which means the "big three" services: Web design, graphic design, and web development. That trifecta covers quite a bit of ground, but can be limiting.

Some of the most lucrative services in the web design business go beyond designing and coding (UX consultancy, usability research, market research, conversion rate optimization, and online marketing being just a few). These services can be the lifeblood of a web design business, yet, as an artisan, it’s incredibly difficult to get these other types of work because your clients view you strictly as a design or code person.

I’m not saying that web designers aren’t artisans. The things we make take incredible skill. Our creations are expected to be beautiful and functional. But if you’re billing yourself as an artisan, make sure that it’s not to your detriment.

Related Content

About the Author

Casey Ark is the founder of Plato Web Design, a custom web design agency located in Harrisburg, PA. He’s also a newspaper columnist. Casey has over 9 years of experience in web design, development and print design.

The post Web Design Isn’t Just About Artistry appeared first on Six Revisions.

Continue reading
0

Testimonial

Thank you so much! We are very happy with our new website. It is easy to use and all of our customers tell us, they love it.

Contact Us

  • 13245 Atlantic Blvd. #4352
    Jacksonville, FL 32225
  • 904-240-5823