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
3414

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
5285
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
3755

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
10991

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
4242

In the first part of this seriesOn data in WordPress, I gave an overview of the WordPress database tables, and which tables are used to store what kind of data. I'll outline how WordPress manages the relationships between that data, In this second part

One-to-many and many-to-many, As you'll see, WordPress uses three kinds of data relationship - one-to-one. I'll look at each of these and what they mean for your WordPress site

One-to-One Relationships

A one-to-one relationship is the simplest relationship of all - it simply means that one record is related to just one other. Data like this is generally stored within one table (although not always, as we'll see later on in this tutorial)

Examples of one-to-one relationships in WordPress include:

  • Post ID and post content
  • Post title and post content
  • Post ID and post slug
  • Comment ID and comment content
  • User ID and username

The list could go on, but the main point is that any two records which are stored in different fields in one row of a table have a one-to-one relationship

Are the least interesting kind of database relationship, One-to-one relationships, however. So without further ado let's move on to a relationship type used a lot by WordPress - one-to-many

One-to-Many Relationships

One to many relationship are very common in databases, and are what makes a database more powerful than a 'flat' table such as a spreadsheet. These occur when one record has a relationship with more than one other record.  

Using a unique identifier to join the two, most one-to-many relationships are created by a link between two database tables, In WordPress. The, So for examplePost_idRecord will be unique to each post stored in theWp_postsTable, but will also be used in theWp_commentsTable to identify which post each comment was posted to. This means that eachPost_idValue will appear only once in theWp_postsTable but could appear multiple times - or not at all - in theWp_commentsTable

Some other examples in WordPress include:

  • Posts and post metadata
  • Posts and users
  • Users and user metadata
  • Taxonomies and taxonomy terms

Again, the list goes on. As this is an important topic in WordPress, let's take a look at the one-to-many relationships in more detail

One-to-Many Relationships Involving Posts

The table which is connected to the most other tables is theWp_postsTable, and most of the relationships these connections create are one-to-many relationships

As you can see in the diagram below, theWp_postsTable is linked to four other tables:

Posts can also have a one-to-many relationship with other posts, in the form of attachments or parent pages, In addition to this

Let's take a look at these relationships in more detail

Posts-to-Posts

Data stored in the, As explained aboveWp_postsThis relationship isn't between what you would think of as posts, table can have a one-to-many relationship with other data in the same table; however, but instead is between posts and attachments or pages and other pages. After all, attachments and pages are post types

An attachment stored in theWp_postsTable will have a record in thePost_parentField, which will be the ID of the post to which the attachment is attached (with apologies for the repetition. ). This is a one-to-many relationship because each attachment will only have one parent, while each post could have many attachments. This will apply even if you've used the Media Manager to add an attachment to another post - only the post to which it was originally added will be stored in the database as its parent

Posts can also have a one-to-many relationship with other posts when one page is specified as the parent of another. In the, The record is stored against the child pagePost_parentField - again this will be thePost_idOf the parent page. This is a one-to-many relationship because parent pages can have many child pages but child pages will only have one parent page

It's important to remember that when considering WordPress data handling, When thinking about relationships between posts like this, posts are not what you might normally refer to as posts. All of which are stored in the, attachments and other post types, They include pagesWp_postsTable

Posts-to-Post Metadata

Post metadata is stored in its own tableWp_postmetaThis table only includes four fields - for thePost_idTheMeta_idThe key and the value. One post can have many items of post metadata associated with it but each post metadata record will only be linked to one post

Posts-to-Comments

Comments also have their own tableWp_commentsEach comment will relate to only one post while each post can have multiple comments stored against it. As with all of the other tables linked toWp_postsThePost_idField is used for the link (in theComment_post_idField inWp_comments)

TheWp_commentsThe comment itself and its approval status, the author's email address, table has 15 fields by default, to store data about the author. It's also linked to theWp_usersTable, as we'll see in the next section

Which I'll come to shortly, stored in another table, Comments also have their own metadata

Posts-to-Users

Each post is attached to theWp_usersTable via theUser_idWhich is stored in the, recordPost_authorField inWp_postsThis is a one-to-many relationship because each post has only one author but each user can be the author of multiple posts

The relationship between posts and users is different rom that between posts and comments or metadata because its nature is of one user, not one posts multiple comments or mete data records, multiple posts. This is why theUser_idField provides the link instead of thePost_idField

One-to-Many Relationships Not Involving Posts

There are also three relationships which are one-to-many and don't involve posts. And users and their metadata, Two of these involve metadata - the relationship between comments and their metadata

Users-to-User Metadata

WordPress stores additional metadata about users in theWp_usermetaTable. This is generally used for non-standard data about users, such as metadata about superadmins in a multisite installation and metadata about the admin colors a user has selected if these aren't the default.  

Most data on users will be in theWp_usersTable. The two tables are linked via theUser_idField and one user record will have multiple items of metadata associated with it, whereas each item of metadata will only apply to one user

Comments-to-Comment Metadata

Again, most data about comments is stored in theWp_commentsTable along with the comment itself, but theWp_commentmetaTable is used to store additional metadata such as data created by plugins like Akismet. The relationship is the same as between users and usermeta

Comments-to-Users

The final one-to-many relationship is between comments and users. TheWp_commentsTable includes theUser_idWhich can be used to store the user ID when a logged-in user has posted a comment, field. Note that this field isn't mandatory for those sites where users don't have to be logged in to comment

Many-to-Many Relationships

Where the many link goes both ways, The final kind of database relationship is the one-to-many relationship. For taxonomy terms (including categories and tags as these are simply taxonomies), This is only used once in WordPress. One post can have many terms assigned to it and one term can be assigned to multiple posts

WordPress creates this relationship in the same way as any other database with manay-to-many database: by using an interim table linking the two tables with the key data. This is theWp_term_relationshipsWhich links the, tableWp_postsTable to theWp_term_taxonomyTable.  

Note: This table also links theWp_linksTable to theWp_term_taxonomyTable, as links behave in much the same way as posts - I'll come to links shortly

This is most easily explained by looking first at the two outlying tables. TheWp_postsTable contains data on each post, while theWp_term_taxonomyIncluding the taxonomy it is in and its ID, table contains data on each term.  

To create a link between a term in theWp_term_taxonomyTable and a post in theWp_postsWordPress creates a record in the, tableWp_term_relationshipsTable, which includes thePost_idAnd theTerm_id(stored asObject_idAndTerm_taxonomy_idRespectively in theWp_term_relationshipsTable). This means that theWp_term_relationshipsCreating the many-to-many relationship, table can include multiple records for each post and multiple records for each term

The diagram below show how this works:

The posts have the following terms:, In the example above
  • Post 1 has terms 1 and 3
  • Post 2 has term 2
  • Post 3 has term 4
  • Post 4 has terms 1 and 3

Term 3 relates to posts 1 and 4, You could also look at it the other way - for example

However things don't stop there. The, There is a fourth tableWp_termsTable. This holds data about each term, namely the name, slug and description for each term. Each term has only one record in theWp_termsSo the relationship between that and the, tableWp_term_taxonomyTable is actually one-to-one. There's no reason this data couldn't be held in the, Theoretically speakingWp_term_taxonomyBut it isn't, table

A Note on Links

Is a feature of WordPress which is far less prominent than it once was, Links, or the blogroll. Since version 3, In fact. 5 links have been turned off in the admin by default; however, they do have their own table: theWp_linksTable.  

This is very similar to theWp_postsTable. Links are a content type very similar to posts) and have the same many-to-many relationship with taxonomy terms, After all

Summary

As we've seen, WordPress uses a variety of relationships to link data in 10 of its 11 database tables. The only table I haven't mentioned here isWp_optionsAs it simply stores data about the site, rather than about content, because that isn't linked to any other tables. I'll cover that in more detail later in this series

By understanding the one-to-one, one-to-many, that can help you to identify ways to manipulate your data and write custom queries in your theme and/or plugins, and many-to-many relationships in WordPress

I'll cover content types in more detail, looking at the types of content stored in the WordPress database and the similarities and differences between them, In the next part of this series

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