jQuery Snippets

Here are list of JQuery code snippets updated ongoing bases

0. Attach Custom Function to HTML Element
(function ($) {
    $('.image_button').click(function(e){
        e.preventDefault();
        var img_src = $(this).find('img').attr('src');
        $('#focus-image').attr('src',img_src);
    });
})(jQuery);

Here, we put everything in parentheses so it executes on page load while passing jQuery object and assign it to “$” symbol. Afterwards, search the html element with class ‘image_button’ and assign click event with our function. This function grabs src from one element and swaps with another

1. How to find events attached to particular element
$._data($('a[href="/node/56/edit"]')[0], "events");

the first element in the _date() function is the element. In our case, that is a link with an attribute of ‘href’ equal to specific path

or with chrome Developer tools select “inspect element’ on the element and then look at the righ

2. How to assign your custom methods to an DOM element
(function(window, document, $) {

var prototype = $.fn,
	    placeholder;
...
            placeholder = prototype.placeholder = function() {
			return this;
		};
            function customFunciton() {
                    //do something
            }
}(this, document, jQuery));

Here, the prototype refers the global prototype heap, so by assigning function placeholder() to the global prototype we make it available to call on any of the element we wish:

$('textarea').placeholder();

So this will assign the scope with all our custom function, so anyone can all the custom function ‘customFunction’ from now on

3. Ways to call your Custom function
$(window).on('load', function () {
    $('[data-ride="carousel"]').each(function () {
      var $carousel = $(this)
      $carousel.carousel($carousel.data())
    })
  })

Here, onload event we search for dom element and then once found execute our costum method ‘carousel’

4. Stop Event

Once you attach an event to an element(i.e.btn-nav-toggle-responsive) to the link tag, you may want it not to go anywhere. In that case, you prevent the default behaviour

$('.btn-nav-toggle-responsive').click(function(e){
		$('.left-sidebar').toggleClass('show-fullsidebar');
        e.preventDefault();
	});

Here, we passing in the event ‘e’, so that we can call ‘preventDefault() that prevents from going to a different link

5. Trigger Event From Console

To trigger events from console:

var my_e = jQuery.Event("some_event_id");
jQuery("body").trigger(my_e)

This will trigger event ‘some_event_id’ from console
To trigger event from code:

$.event.trigger({
            type: "reset_image_ds"
        });
6. Load JS Library from Console

There are at least two ways:

jQuery.getScript('http://dev-ckeditor/ckeditor/ckeditor.js');

In this approach we use jQuery libarary to load script ckeditor.js. An altertnative, is:

var script= document.createElement('script');
script.type= 'text/javascript';
script.src= 'script.js';
document.head.appendChild(script);

For stylesheets:

document.appendStyleSheet('/path/bootstrap.min.css');
7. Decode String in jQuery

Perhaps, you garbed html text just to learn it is in the encoded form. To decode it back into html tags:

var decodedHtml = jQuery('<textarea/>').html("SOME-STRING-WITH-HTML-CHARACTERS").val();

This will take all html character symbols and convert to appropriate tags names

8. jQuerify HTML

Perhaps, you have raw HTML and you would like to make into jQuery elements, so you can take advantage of jQuery Lib:

var b = jQuery(decodedOldHtml);
var output = b.find('.wrapper-end').html();

This will take each html element and wrap into jQuery objects. Afterwards, you can call jQuery Api as we did here to find certain element(i.e. wrapper-end)

Note: You will get error –

“Uncaught Error: Syntax error, unrecognized expression:”

if the content you are trying to jQuerify(i.e. decodeOldHtml) is not wrapped into any tags. The good practice always wrap into some tags before jQuerify as following:

jQuery('<div>' + decodedOldHtml + '</div>');

This way you will avoid the Syntax error all times for text only content. The more important it will not break on ‘find’ calls by putting it all in one jQuery object. For more, see jquerify-string-to-find-element-fails. At last, ensure the wrapping tags are not ‘body’ because jQuery ignores the ‘body’ tag wrapper

9. Make jQuery Sandbox

It may be too long of name – jQuery to type each time, so what often is done is create jQuery sandbox and pass the “$” as jQuery object:

(function ($) {
       console.log($('find-me').html());
})(jQuery);

Here,we first wrap our code into parentheses, so besides parsing it is also executed. In addition,for our code block, we pass in jQuery object by putting at the end and assigning it to “$” since its the parameter in the function.

Often times your code will do some manipulation on DOM, then you probably like to make sure DOM is loaded before the code in your sandbox is executed:

jQuery(document).ready(function($) {
       console.log($('find-me').html());
});

So, we attach event ‘ready’ before executing the sandbox

At last, it may be the case you will merge your code with other javascript files, then add ‘;’ in front to avoid breaking the execution:

;jQuery(document).ready(function($) {
       console.log($('find-me').html());
})(jQuery);
9. Remove and Replace DOM Elements

Some of the most common manipulations to the DOM is remove & replace. To remove element with its children:

$('.find-me').empty();

This will remove the element ‘.find-me’ and all of the children(the tree) below
To replace an element or often times remove the element but not the children:

var cnt = $('.find-me').contents();
 $('find-me').replaceWith(cnt);

This will take out the element ‘.find-me’ but keep all the children intact.

JS Widget Boilerplate

There are probably several ways to setup and organize code for your JS widget. Here is one I find common and works well for me:

if (!window.VIRTUAL_HOSTY) {
    /**
     * @singleton
     */
    window.VIRTUAL_HOSTY = (function ($) {

        //private variables come here...
        var base_url = window.BASE_URL || '/',
            log_on = false;

        //private function to initiate your widget
        var _init = function () {
            $(window.document).ready(function ($) {
               _parse_url();
            });
        }

        //public interface...the API for your widget
        return {
            set_base: function (some_val) {//function to set private variable
                base_url = some_val;
            },
            get_base: function () {//function to retrieve private variable
                return base_url;
            },
            init: function(){//public function called to initialize widget
              _init();  
            },
            turnOnDebug: function () {//public function to turn on logging
                log_on = true;
            }
        }
    })(jQuery);
}

Here we create global variable VIRTUAL_HOSTY that is the access point of our Widget. All the interaction happens via this variable. The VIRTUAL_HOSTY returns an object. All of the functions specified in this object is public. Everything else is private since we wrapped it into parentheses “()” making the scope execute at the time of load accessible only with the references in the returned object VIRTUAL_HOSTY

Once you have the widget lib file imported you can call on it:

<script>
VIRTUAL_HOSTY.turnOnDebug();
VIRTUAL_HOSTY.init();
</script>

Here,we turn on logging and then initiate our widget VIRTUAL_HOSTY

Importing and Exporting Products in Drupal Commerce

In prevous post Drupal Commerce Install and Setup, we went over on incorporating commerce functionality with Drupal and creating/displaying Products. In this post, we go over how to import/export Drupal commerce “products” as well as “product displays”. Important to note, there are “products” and then there are “product displays”. The “product display” is displaying one to many products referenced by the display. It is plain Drupal node with an extra field of type – product reference. The “product” is specific type provided by Commerce module.

Initially, I was thinking to use features for import and export products in drupal commerce store but it doesn’t have the capability for products to be featured, so the solution for us was using Views data export to export products and Commerce_feeds for importing products

Exporting Commerce Products

So, you have several products that you created with Drupal Commerce product installed and its UI. To export those products, we first create a view containing the products and then export this view via Views data export into CSV file

a) Install views_data_export and dependent modules
drush dl views_data_export
drush dl image_url_formatter
drush en views_data_export image_url_formatter
b) Create a view with products you like to export

Create a view with products you like to export. The view has to list each product field you like to export, so add each product field. Here are other specifics of the view:

  • ‘size’ is set to unlimited
  • give a display name in the Administration settings(in our example ‘theme_export_display’)
  • select image fields with field formatter ‘image_url’ (see module image_url_formatter) with setting ‘nothing’ for linking
    • Image url and ‘URI Path’ for Uri path.
  • ensure price is of type “raw amount”(i.e 8000 for $80.00)
  • ensure product status output format is ‘0/1’
  • Do not include Product ID.
  • keep field labels, so there is header in the export file
  • Format type for taxonomy fields are ‘link’
b) Configure View for export
  1. Add a new “Data export” display to your view.
  2. Change its “Style” to the desired export type. e.g. “CSV file”.
  3. Configure the options (such as name, quote, etc.). You can go back and do
    this at any time by clicking the gear icon next to the style plugin you just
    selected.
  4. Give it a path in the Feed settings such as “path/to/view/csv”.
  5. Attach this view to the view containing the products you like to export by
    updating the “Attach to:” option in feed settings.
  6. Ensure “Items to Display” is set to “Display All”
c) Generate the file via drush command
drush views-data-export --format=csv VIEW-NAME VIEW-DISPLAY-NAME exports/product-exports/theme_export_Jan11_2014.csv --quote-values --strict=0 --header-row

This will take the view and create csv file from it.

Import Commerce Products

Once you have the csv file with commerce products an export generated as described above, we going to use Commerce_feeds for import this products in another Drupal instance

a) install commerce_feeds module and its dependent modules
drush dl feeds
drush dl feeds_ui
drush dl commerce_feeds
drush en feeds feeds_ui commerce_feeds

This install all the necessary artifacts for using commerce_feeds to import Drupal commerce products

b)Create New Feed Importer

Create a new feed importer named “Product Importer” at Administration -> Structure -> Feeds Importer -> Add Importer. Here are specifics of this importer:

  • Change the parser to “CSV Parser”
  • Change processor to “Commerce Product Processor”
  • In “Commerce Product Processor” settings use product type “product” (or whatever your product is) and change the “Author” to your username.
  • ensure Fetcher is of type ‘file upload’
  • In “mapping”, map:
    • SKU -> Product SKU
    • Title -> Product Title
    • Price -> Price: Amount
    • Image -> Image
    • Set ID as unique target.
    • …//so on with other product fields in the .csv file

c) Run import

Go to ‘/import’ url, use the Product Importer and import your products
Note: before importing, make sure you have copied the artifacts into the same location you importing from if there is any custom fields with artifacts

Exporting Product Displays

Everything would be the same if there wouldn’t be product reference field that links the multiple products to the particular display it is for. To import/export ‘product’ field, we will utilize modules ‘feeds_temper’ and ‘feeds_tamper_ui’ modules.

drush dl feeds_tamper
drush en feeds_tamper feeds_tamper_ui

Their responsibility is to take list of SKUs part of ‘products’ field and create multiple product references for each display at the time of import

a)Create a view

Create view with displays to export(i.e. view ‘theme_display_export’ with display name – ‘display_export’). Here are some important details about the view:

  • ensure result size of node is set to unlimited
  • ensure the output formatter for field ‘product’ is set ot ‘SKU no link’
  • ensure label is attached, so it generates csv with a header row

b) Configure View for export
  1. Add a new “Data export” display to your view.
  2. Change its “Style” to the desired export type. e.g. “CSV file”.
  3. Configure the options (such as name, quote, etc.)
  4. Give it a path in the Feed settings such as “path/to/view/csv”.
  5. Attach this view to the view containing the product displays you like to export by
    updating the “Attach to:” option in feed settings.
  6. Ensure “Items to Display” is set to “Display All”
c)Run Export

Export Product Displays into .csv file by calling drush command as following:

drush views-data-export --format=csv theme_display_export display_export exports/display-exports/display_export_Jan11_2014.csv --quote-values --strict=0 --header-row

This will generate .csv file with product displays

Import Product Displays

Once you have the .csv of product displays, we import them in another Drupal instance. Here are the steps:

a)Create Product Display Importer

First, create product display Importer by going to ‘Admin’->’Structure’->’Feeds Importer’ -> ‘Add New Importer’. Some important things to watch for:

  • ensure this is Nodes Processor under the Process
  • ensure Fetcher is set to ‘file upload’
  • create mappings
b)Create Rule for ‘product’ field

Create Rule for ‘product’ field by clicking on the ‘Tamper’ tab and then ‘add plugin’ under the field ‘product’ to ‘Product:SKU’ mapping or however you named the relationship mapping between display to products.
Note: ensure the rule is of type ‘list/explode’

c)Run importer

At last, run importer by going to ‘/importer’ url and selecting the product display importer following the UI

Troubleshooting

1. Unknown option: –header-row. See `drush help views-data-export` for available options.To suppress this error, add the option –strict=0

Make sure to add –strict=0 to the command or in the ‘alias’ configuration file

2. Exported file’s header comes up empty(“”,””,””…)

Make sure to include label for each field, otherwise the header is empty

3. The Images field is coming up empty when exported into csv file

This because there is need to have a special field formatter to format image into url. Module image_url_formatter does exactly that

4. Price is not importing accordingly all zeros

-ensure price is of type “raw amount”(i.e 8000 for $80.00) in the exported CSV file

5. Product Status is always importing into ‘Disable’ State.

Ensure the exported value of product status is in format of ‘1/0’ that can be configured in the view under ‘output format’

6. Images are not displayed in Admin UI.

Ensure that the exporter is set to export image fields into ‘URI path’ output format that is provided by a separate module ‘image_url_formatter’

7. “A product with SKU some_sku could not be found. Please check that the product exists or import it first”(resulting on display only pointing to first product but not all)

Answer: ??? I am not sure…still figuring this out

8. ‘PDOException: SQLSTATE[42S02]: Base table or view not found:’

Make sure cache is cleared after view created/updated. Also ensure your view returns results

9. Missing Feeds plugin FeedsCommerceProductProcessor

This error come up when there was missing ‘commerce_feeds’ module. After installing and enabling it, the error goes away

10. User by id ‘864’…

This happens when running importer with user configured that doesn’t exist on current Drupal instance. Go to admin/structure/feeds/NAME_OF_YOUR_IMPOTER/settings/FeedsNodeProcessor and update user with any user currently present

11. Warning: is_dir(): Unable to find the wrapper “private” – did you forget to enable it when you configured PHP? in file_prepare_directory()

Ensure that the private dir is configured for your Drupal instance (see “Private file system path” at admin/config/media/file-system)

12. Target is missing for Node Processor of Importer

Make sure the field is present fro the content type you are importing

13. Exports exactly 10 items only no matter what the pager is set to

This is happening when the view exported is not of type ‘Data Export’. Ensure exporting view is of type “Data Export”

Reference

  • https://drupal.org/project/commerce_feeds
  • http://drupal.stackexchange.com/questions/87039/how-can-i-import-and-export-commerce-products-in-drupal-7
  • https://drupal.org/node/622698
  • https://drupal.org/project/commerce_feedsmulti
  • http://www.drupalcommerce.org/node/467

Taking CKEditor a Apart

In this post, we dive into the code of CKEditor to learn internals. We needed to do so to understand how the empty tags are being removed by default. This was not acceptable behavior because frameworks like Twitter Bootstrap or Foundation use empty tags part of the layout.

The dev version none minimized CKEditor are located at https://github.com/ckeditor/ckeditor-dev

Boot from Console

When troubleshooting or learning, it always easer to move everything else out of the way but the thing of your interest. In our case, we decided to run CKEditor without Drupal in the browser console for quick, easy changes. In the following are steps to boot CKEditor from console:

1. Disable Drupal

To take Drupal out of way, we disable the CKEditor module

2. Import CKEditor

There were several ways to load CKEditor all of which are listed here

A)From HTML Layout
By inserting the following in top of your page within ‘head’ tag

<script src="http://dev-ckeditor/sites/all/libraries/ckeditor/ckeditor.js?n83a0r"></script>

B) From Drupal API
By executing the following code in hook_preprocess_page() or any other appropriate place for that matter

drupal_add_js('http://dev-ckeditor/sites/all/libraries/ckeditor/ckeditor.js', array('scope' =>; 'header', 'type' =>; 'external'));

C)From JS Console
By calling the following, ckeditor library will be loaded

jQuery.getScript('http://dev-ckeditor/ckeditor/ckeditor.js');

NOTE: In console, I run into errors when ckeditor.js was trying to load its dependences such ad load.js due to unable configure base path. I believe it should work if using the production ckeditor.js instead the development version that is not compiled into one

This will import the library as well as initialize CKeditor after which making the global variable CKEDITOR available and ready, so we can call CKEditor API

Let’s check the status

CKEDITOR.status

It gives status – ‘loaded’ on success and its ready to start ckeditor
There are 4 different status – loaded, basic_load, ???

4. Load Into Textarea

To start up CKEditor in the textarea is as simple as initializing it that can be done from console as following:

CKEDITOR.replaceAll();
//or particular element with certain id
CKEDITOR.replace('id_name');

Here, we start up all of the textareas by calling replaceAll(). In the second, we enable CKEditor on certain element not necessaryly ‘textarea’ with id of ‘id_name’. This should bring up the text editor

Other Stuff

1. Editor Instances
CKEDITOR.instances

This will display all the ckeditor instances

2. See Editor Configuration

From console:

var editor = CKEDITOR.instances.editor1;
alert( editor.config.skin ); // e.g. 'moono'
3. Filters

To see what input is about to be filtered:

CKEDITOR.instances['edit-body-value'].element.$

Setting custom filters:

var filter = new CKEDITOR.filter( 'p strong em br' );
editor.setActiveFilter( filter );
...
...
// Create standalone filter passing 'p' and 'b' elements.
		 *		var filter = new CKEDITOR.filter( 'p b' ),
		 *			// Parse HTML string to pseudo DOM structure.
		 *			fragment = CKEDITOR.htmlParser.fragment.fromHtml( '<p><b>foo</b> <i>bar</i></p>' ),
		 *			writer = new CKEDITOR.htmlParser.basicWriter();
		 *
		 *		filter.applyTo( fragment );
		 *		fragment.writeHtml( writer );
		 *		writer.getHtml(); // -> '<p><b>foo</b> bar</p>'

For hooking into all enable active filters:

editor.on( 'activeFilterChange', function() {
     if ( editor.activeFilter.check( 'cite' ) )
        // Do something when <cite> was enabled - e.g. enable a button.
     else
        // Otherwise do something else.
   } );

To disable filters:

//before the editor is initiated
 CKEDITOR.config.allowedContent = true;
textarea_settings = Drupal.ckeditorLoadPlugins(textarea_settings);
CKEDITOR.replace(textarea_id, textarea_settings);

Here, the textarea_id is the ‘id’ of the element(textarea or div). I am not sure the textarea_settigns???

5. Drupal and CKEditor

In the modules/ckeditor/ckeditor.utils.js, is where the CKEditor instances are created and attached to textarea.
From ckeditor.utils.js, the function CKEditor.replace is called that is defined in the themedui.js that calls CKEDITOR.editor from editor.js

6. Fragmenting in CKEditor
var fragment = CKEDITOR.htmlParser.fragment.fromHtml( '<p>foo<b>bar</b>bom</p>' );
 fragment.forEach( function( node ) {
 console.log( node );
} );
		 *		// Will log:
		 *		// 1. document fragment,
		 *		// 2. <p> element,
		 *		// 3. "foo" text node,
		 *		// 4. <b> element,
		 *		// 5. "bar" text node,
		 *		// 6. "bom" text node.
		 *
		 * @since 4.1
		 * @param {Function} callback Function to be executed on every node.
		 * **Since 4.3** if `callback` returned `false` descendants of current node will be ignored.
		 * @param {CKEDITOR.htmlParser.node} callback.node Node passed as argument.
		 * @param {Number} [type] If specified `callback` will be executed only on nodes of this type.
		 * @param {Boolean} [skipRoot] Don't execute `callback` on this fragment.
7. Stop Removing Empty Tags

There is defined list of tags that is going to be removed if empty(see dtd.js and $removeEmpty). To ensure the certain empty tag are not being removed, add attribute ‘data-cke-survive’:

<span data-cke-survive="true" ></span>

or pass the following to the CKEditor configuration file:

CKEDITOR.dtd.$removeEmpty['span'] = 0;
CKEDITOR.dtd.$removeEmpty['TAG-NAME'] = 0;

This make the empty tags to not being removed
NOTE: If you working with the production CKEditor.js, you will have to alter section starting “CKEDITOR.dtd” as following:

 CKEDITOR.dtd=function(){var a=CKEDITOR.tools.extend,e=function(a,d){for(var [...]
span:0//set the 
TAG:0
..

You will have to grep the CKEditor.js file for section starting “CKEDITOR.dtd” and then change value from 1 to 0 for each tags you like to not be removed if empty(see above example for ‘span’ tag)

8. Update Styles

Here is an example for console to update styles:

var editor = CKEDITOR.instances['edit-body-und-0-value'];
editor.document.getBody().setStyle('font-size','30px');

Here, we start by retrieving the CKEditor Instance with id “edit-body-und-0-value”. Afterwards, we set the font-size.

if you like append the whole style sheet than:

editor.document.appendStyleSheet('/sites/all/path/to/css/bootstrap.min.css')
9. Stop Auto Wrap

The default behavior is for CKEditor to wrap text into paragraph tags. To stop that:

//from console
CKEDITOR.config['autoParagraph'] = false

–OR–
In the ckeditor.config.js file:

config.autoParagraph = false

This will stop automatically wrapping every element into paragraph tag. You can also set to add ‘br’ or ‘div’ instead of pargraph tags as following:

CKEDITOR.config['entermode'] = CKEDITOR.ENTER_BR;//or config.entermode=2 in config.js
CKEDITOR.config['entermode'] = CKEDITOR.ENTER_DIV;//or config.entermode=23 in config.js

This will add BR or DIV tag to every element

To stop wrap a certain element such as anchor tag into paragraph tag:

                delete CKEDITOR.dtd.$inline['a'];

This will change the declaration of the anchor tag to not be an inline tag, thus, no any wrapping takes place

Stop remove Empty Anchor tags

To stop removing empty tags, add the attribute – “data-cke-survive” to the anchor tag as following:

<a href="#" data-original-title="github" class="github" data-cke-survive="true">

Or ensure attribute “href” is not present as following:

<a data-original-title="linkedin" class="linkedin">
Stop Wrap UL around LI

By default, CKeditor is wrapping UL element around LI if one is not present. To stop alter CKEDITOR.dtd.$listItem in DTD.js

//OLD:$listItem: { dd: 1, dt: 1, li: 1 },
        $listItem: { dd: 1, dt: 1},

–OR–
By editing CKEditor directly will cause problems sooner or later. Instead, hook into CKeditor and update from outside

if(window.CKEDITOR){
                CKEDITOR.on('instanceCreated', function (ev) {
                  //make sure LI is not wrapped within UL
                    delete CKEDITOR.dtd.$listItem['li'];
                    delete CKEDITOR.dtd.$intermediate['li'];    
                }
}

Here, we hook into the event triggered when CKEditor instance is created to remove “li’ tag from inline tags list.
By removing the LI form the list, you ensure that CKEditor is not wrapping LI elements with additional “UL”. For production, you will have to grep ckeditor.js file to make the change since everything is compiled into one JS file

Hook Into Events

There are lot of events available either on CKEDITOR App variable – CKEDITOR or on each editor instance itself

To listen for event on CKEDITOR APP variable

<script>
CKEDITOR.on( 'instanceReady', function( ev ) {
    editor = ev.editor;
    if(editor.name == 'edit-body-value'){
         console.log( 'appending style - sites/all/themes/metronic/assets/drupal/custom_metronic.css' );
         editor.document.appendStyleSheet('/path/custom_metronic.css');
}
}
</script>      

Here, we sign up for event “InstanceReady” to append custom stylesheets.

To listen for event on editor instance itself

CKEDITOR.on( 'instanceCreated', function( ev ) {
    if(ev.editor.name == 'edit-body-value'){
       ev.editor.on( 'contentDom', function(){
         var ck_dom = ev.editor.document.$;
         var s0= ck_dom.createElement( 'script' );
         s0.type = 'text/javascript';
         s0.onload = function() {
             console.log( 'script /path/jquery-1.11.0.min.js loaded' );
         }
         s0.src = '/path/jquery-1.11.0.min.js';
         s0.$ = s0;
         ck_dom.head.appendChild(s0);
      } );
   }
 });   

Here, we first use event on the CKEDITOR App itself to get handle of the editor obj. Once we got the editor object, we sign up for an event “contentDom” that is triggered once the DOM of editor content is constructed. During this event we are importing JQuery lib

See What’s Going On

To see what is filtered, processed and actually sitting in the editor content(DOM) at any give time from console:

var editor = CKEDITOR.instances['edit-body-value'];
editor.document.$.body //to see what is within BODY tags
editor.document.$.head //to see what is within HEAD tags

Here, we first retrieve the particular textarea of our interest and then navigate to the BODY or HEAD. It will display the DOM in real time

How To Find Which Config.js CKEditor Is Using

To see which config.js the CKEditor is actually using, once the instance is up, go to console and type:

CKEDITOR.config.customConfig
CKEDITOR.instances['edit-body-value'].config.customConfig

This will print out the path and the name of config file CKEditor is using. Note, there may be different config files used at the same time. The CKEDITOR app using one and then each different editor instance(i.e.CKEDITOR.instances.editor1) using another one(perhaps one loaded from the theme folder,etc)

Some Tags are Removed

you can disable the filter with allowedContent configuration as described above. You can also disable processing on a particular tag as following:

  • data-cke-filter=”off”
  • data-cke-processor=”off”
  • autocomplete=”off”

This will apply for the tag and all of its children

Manipulate Editor Content

To remove some element from the the editor:

var element = ev.editor.document.getById( 'ckeditor-wrapper-end' );
element.remove(true);

This will remove element with id ckeditor-wrapper-end while keeping parent and children elements intact.
The better way API for manipulated data of editor is available on “editable” object:

var editable = CKEDITOR.instances['.textId'].editor.editable();
if(editable.find('ckeditor-wrapper-end').count()) editable.find('.ckeditor-wrapper-end').remove();

As you can see, the “editable” API has similar API as jQuery. Please, reference editable.js for complete list of function available.

Update Editor

To update editor content:

var editor = CKEDITOR.instances['TEXTAREA-ID'];
var newHtml = '<p>Hello World!</p>";
editor.setData(newHtml,{
       callback: function() {
         this.updateElement();
        }
});
$('textarea#TEXTAREA-ID').html(newHtml);

Here, we first retrieve editor and then update its content via setData() API. At last, the textarea itself is updated in the DOM

Summary of Events

Event Scope comments
loaded editor triggered once editor is loaded before initiating the editor content
instanceLoaded CKEDITOR triggered once CKEDITOR is loaded
customConfigLoaded editor
configLoaded editor
destroy editor
save editor
key editor
dblclick editor
doubleclick editor
click editor
beforeGetData editor Handle the load/read of editor data/snapshot.
getSnapshot editor sets event data from the editor data
saveSnapshot editor ???
lockSnapshot editor ???
unlockSnapshot editor ???
afterSetData editor sets the data from editor data
loadSnapshot editor takes event data and sets it to the editor data
beforeFocus editor Delegate editor focus/blur to editable.
insertHtml editor takes the data from event and inserts in CKEDITOR.DOM via insertHTML()
insertElement editor takes the data from event and inserts in CKEDITOR.DOM via insertElement()
insertText editor takes the data from event and inserts in CKEDITOR.DOM via insertText()
beforeSetMode editor Before any mode is set on the Editor(i.e. editor.mode)(see plugin.js)
beforeModeUnload editor after mode is set on the editor(i.e. editor.mode)(see plugin.js)
beforeGetModeData editor before getting data after setting the mode(see plugin.js)
afterModeUnload editor passing data to be able update at last step in mode setup(see plugin.js)
toHtml editor at the time data is process including filters applied(see htmlprocessor.js)
dataFiltered editor after some filter applied(see htmldataprocessor.js)
dataReady editor when data is ready before processing and filtering(see htmldataprocessor.js)

To fire any of the events:

editor.fire( 'saveSnapshot' );

Here, we trigger one of the events – saveSnapshot attached to the editor.
To listen an event:

			editor.on( 'saveSnapshot', function( evt ) {
                                //do something with evt.data or evt.data.contentOnly
			} );

Here we listen of event – save Snapshot and then grab the new data via evt.data or evt.data.contentOnly

Encode and Decode

To decode or encode a string:

var result = CKEDITOR.tools.htmlDecode(editor.getData());
var encodedText = CKEDITOR.tools.htmlEncode(result);

Here, the editor data is decoded and encoded. See CKEDITOR.tools for much more utilities

Troubleshooting

1.Resource interpreted as Script but transferred with MIME type text/html: “http://dev-ckeditor/node/add/core/loader.js”

This happened when I was trying to load CKEditor library from console and the CKEditor was trying to load its dependencies. The dependency URL(path) came up wrong resulting in this error. I tried to set CKEditor Base path but it didn’t work because the base path needed to be set before the CKEdiotr library is loaded. I tried that and it didn’t work

The changes doesn’t take an effect

Make sure you clear browser cache as well as Drupal cache for the most current config.js loaded

Adding CKEditor Plugins Manually in Drupal

CKEditor has a lot of new good plugins that doesn’t have supporting Drupal module for easy integration. In this post, we go over how to integrate any native CKEditor plugin without supporting Drupal module present. Specifically, we cover how to install the plugin as part of CKEditor module and how to do the same but as separate custom module. In this post, we have CKEditor installed as a stand alone module not part of Drupals WYSIWYG install. How to install stand alone editor such as CKEditor, please, see post Wysiwyg With Ckeditor In Drupal

To make it more practical, lets install CKEditor plugin – Leaflet that adds map functionality to the editor

Add Plugin Part of CKEditor Module

1.) Download and unzip the plugins here (depending on your setup):

“sites/all/modules/ckeditor/plugins” or “sites/all/modules/contrib/ckeditor/plugins”.

After that, you should have the following folder structures:
“ckeditor/plugins/leaflet”
“ckeditor/plugins/lineutils”
“ckeditor/plugins/widget”

2.) We should now activate the new plugins and add them to the toolbar.

This is done by configuring the Ckeditor Profile Settings, in which by default is located in:
“admin/config/content/ckeditor/edit/Advanced”
“admin/config/content/ckeditor/edit/Full”

A. Activate the Plugins
In EDITOR APPEARANCE >>> Plugins section:
Enable the corresponding checkboxes for the Leaflet, Line Utilities, and Widget plugins.
These are the texts displayed adjacent to their checkboxes:
“Plugin file: leaflet”
“Plugin file: lineutils”
“Plugin file: widget”

B. Add them to the Toolbar
We should make the activated plugins visible in the toolbar,
skipping this step will make the Leaflet plugin inaccessible in the toolbar.

In EDITOR APPEARANCE >>> Toolbar section:
Drag the Leaflet Maps icon (black-colored) from the ‘All Buttons’ section to the ‘Used Buttons’ section.

We need to configure the Leaflet Maps icon only since the Line Utilities and Widget plugins
have no toolbar icons and they will just load in the background.

3.) Then, click the Save button. Clear the Drupal’s overall cache AND clear the browser’s cache.

Clearing the browser’s cache is also very important since the CKEditor’s JS and CSS assets/components are cached also in the browser.

As indicated above, using the “sites/all/modules/ckeditor/plugins” or “sites/all/modules/contrib/ckeditor/plugins”
will work with no additional custom hooks programming since by default CKEditor utilize that folder
in the CKEditor Global Profile Settings: “admin/config/content/ckeditor/editg”.

Adding Plugins As Stand Alone Module

The above approach requires the plugins to be part of the CKEditor Module, but it may not be desirable especially in case for distributing your custom module that requires one of the CKEditor plugin. In that case, its good idea to keep CKEditor plugins in stand alone module part of your custom module perhaps. Here is how to do so:

Lets say your custom module name is ‘custom-module’ and you are going to keep CKEditor plugins in sub-folder ‘ckeditor-plugins’, then the destination location would be:
“sites/all/modules/custom-module/ckeditor-plugins”

After that, you should have the following folder structures:
“sites/all/modules/custom-module/ckeditor-plugins/leaflet”
“sites/all/modules/custom-module/ckeditor-plugins/lineutils”
“sites/all/modules/custom-module/ckeditor-plugins/widget”

2.) Hook Plugins Into CKEditor

Next, lets hook the plugins into CKEditor via hook_ckeditor_plugin as following in custom-module.module file:

/********CKEditor plugins********/
function custom-module_ckeditor_plugin()
{
    $module_path = drupal_get_path('module', 'custom-module');
    $plugins     = array();

    $plugins['leaflet'] = array(
        'name'    => 'leaflet',
        'desc'    => t('DesignsSquare.com: CKEditor: Leaflet plugin'),
        'path'    => $module_path . '/ckeditor-plugins/leaflet/',
        'default' => 't',
        'buttons' => array(
            'leaflet' => array(
                'label' => 'Leaflet Map',
                'icon'  => 'icons/leaflet.png',
            ),
        ),
    );

    $plugins['lineutils'] = array(
        'name'    => 'lineutils',
        'desc'    => t('DesignsSquare.com: CKEditor: LineUtils plugin'),
        'path'    => $module_path . '/ckeditor-plugins/lineutils/',
        'default' => 't'
    );

    $plugins['widget'] = array(
        'name'    => 'widget',
        'desc'    => t('DesignsSquare.com: CKEditor: Widget plugin'),
        'path'    => $module_path . '/ckeditor-plugins/widget/',
        'default' => 't'
    );

    return $plugins;
}

Here, the variable ‘name’ is as specified in the plugin.js that is part of all CKEditor Plugins (look for – CKEDITOR.plugins.add(“NAME”‘) – in the plugin.js for each plugin). The button index(highlight line) has to be exactly as specified in the plugin.js(look for ‘ui.addButton(“BUTTON-NAME”). The variable ‘icon’ specifies the location for the image of the button. Ensure it is there.

Afterwards, the plugins will show up in the CKEditor Configuration section(admin/config/content/ckeditor/edit/Full(or Advance). The rest is the same from the first approach where you would check the plugins in ‘Plugins’ section at admin/config/content/ckeditor/edit/Full(or Advance) and then move the icon into Toolbar.

Troubleshooting

1. Unable To Add Icons in Toolbar

Turn off the toolbar and add icons manually. To turn off the toolbar go to the admin/config/content/ckeditor/editg and select ‘disable’ for ‘Use toolbar Drag&Drop feature’. I am guessing that the toolbar misbehaves because CKEditor Module imports JQuery UI sortable lib that is already imported once by the core. As result, it causes js fatal errors. I am out of time to verify.

2. Uncaught TypeError: Cannot read property ‘icons’ of null

Ensure the name of the plugin as specified in the plugin.js(i.e. CKEDITOR.plugins.add(‘Here-comes-name’,) is the same used in the hook_ckeditor_plugin(). Set a break point with condition of ‘s === null’ where s is the variable the attribute ‘icons’ is called on(i.e s.icons) to help identified which plugin is causing issue in ckeditor.js

References:

  • http://docs.cksource.com/CKEditor_for_Drupal/Enterprise/Drupal_7/Plugins
  • https://github.com/ranelpadon/ckeditor-leaflet/blob/master/Installation%20Guide.txt

Solving Menu Import Issue when Featuring Sample Data in Drupal

[UPDATE] – alias menu import module was rolled into “Sample Data” module that is of newer version. it is listed on Drupal site
Here is post about it – http://margotskapacs.com/2014/07/importexport-sample-data-and-assets-for-kickstart-all-via-features/

Currently, if you would like to export and import the sample data( like kickstart) via features, then the menus for your data breaks because it uses the hard coded path(i.e. node/id) and the new instance has different path for the nodes imported. To my understanding there is a work in place to fix this in future release of features by using the path uuid but of January, 2014 that is not available at least in stable version

The solution presented here utilizes ‘pathauto’ and its generated alias path for each node to do the mapping between the same nodes on two different Drupal instances. In another words, we export existing menu structure with alias path as reference. Once these nodes are imported(before menu import script is run) on the another Drupal instance, it automatically generates the default alias path(for custom alias, please, section ‘If Custom Path Alias…’ below). Since these alias path are the same between the drupal instances, the installation script looks up the mapping when structuring and building menu.

To maintain the existing menu structure, you will have to run a script that will generate the structure used by the menu importer script as showed in this post. This may be improved in future but author didn’t have time at this time and may not be a value since there is path uuid project in progress that will be the better solution in long run

In addition, the path for nodes has to be unique as well.

Requirement:

  1. Pathauto module has to be installed and enabled
  2. Each node have to have path alias or setting ‘Generate automatic URL alias’ selected under ‘Url path settings'(this is default behavior once ‘pathauto’ enabled, so you should need to do anything unless you have nodes present and you just installed/enabled ‘pathauto’. In that case, you have to go into each node and check the setting ‘Generate automatic URL alias’)
  3. The nodes has to be already imported before running the menu_import on the new Drupal instance
  4. (Recommended) When importing Nodes via features, ensure the StrongArm variable or configuration of ‘pathauto_node_pattern’ is also imported. This ensures that for imported nodes the generated alias path is the same as the one in current Drupal instance. This is especially important if the alias pattern for nodes is not the default one

The solution consists of two parts:

Part I: Build Exportable Menu

To build current menu structure for menu of your choice run the following script in “PHP Execute” block on Drupal instance you want to export the menu from(copy/past and then specify menus to export on line 3):

include_once DRUPAL_ROOT . '/includes/utility.inc';
//specify menues to export
$menus = array('menu-bottom-menu', 'main-menu');
$mainMenuArray = array();
//mlid to alias map
$alias_map = array();
$menu_items = array();

$output = "function _menus_altered(){ \n";
$output .= "return array (\n";

foreach ($menus as $menu) {
    $links = db_select('menu_links', 'ml', array('fetch' => PDO::FETCH_ASSOC))
        ->fields('ml')
        ->condition('ml.menu_name', $menu)
        ->condition('ml.hidden', '0')
        ->orderBy('weight')
        ->execute()
        ->fetchAll();

    foreach ($links as $key => $link) {
        $links[$key]['options'] = unserialize($link['options']);
    }
    $mainMenuArray = array_merge($mainMenuArray, $links);

    $mlid_ins = db_select('menu_links', 'ml')
        ->fields('ml', array('mlid', 'link_path'))
        ->condition('ml.menu_name', $menu)
        ->condition('ml.hidden', '0')
        ->execute()->fetchAllAssoc('mlid', PDO::FETCH_ASSOC);

    $menu_items = $menu_items + $mlid_ins;

    $menu_ins = menu_load($menu);
    if ($menu_ins) {
        $menu_item = 'array(';
        $menu_item .= "'menu_name' => '" . $menu_ins['menu_name'] . "',";
        $menu_item .= "'title' => '" . str_replace("'", "\'", $menu_ins['title']) . "',";
        $menu_item .= "'description' => '" . str_replace("'", "\'", $menu_ins['description']) . "'";
        $menu_item .= ")";
        $output .= $menu_item . ",\n";
    } else {
        watchdog(WATCHDOG_NOTICE, "menu: $menu not found");
    }
}
$output .= "    );\n";
$output .= "}\n\n\n";

//generate the map between mlid and alias path
foreach ($menu_items as $mlid => $options) {
    $alias_map[$mlid] = drupal_get_path_alias($options['link_path']);
}
$output .= "function _menu_installed_items() {\n";
$output .= "return array (\n";
foreach ($mainMenuArray as $key => $link) {
    $alias_path = drupal_get_path_alias($link['link_path']);

//prepare link to pass in menu_link_save()
    $link_export = $link;
    $link_export['link_path'] = $alias_path;
    unset($link_export['mlid']);
    unset($link_export['router_path']);
    unset($link_export['options']['identifier']);
    $link_export['plid'] = ($link_export['plid']) ? $alias_map[$link_export['plid']] : $link_export['plid'];
    $link_export['p1'] = ($link_export['p1']) ? $alias_map[$link_export['p1']] : $link_export['p1'];
    $link_export['p2'] = ($link_export['p2']) ? $alias_map[$link_export['p2']] : $link_export['p2'];
    $link_export['p3'] = ($link_export['p3']) ? $alias_map[$link_export['p3']] : $link_export['p3'];

    $output .= "'" . $alias_path . "' => " . drupal_var_export($link_export) . ",\n";
}
$output .= ");\n";
$output .= "}\n";

drupal_set_message("<textarea rows=100 style=\"width: 100%;\">" . $output . '</textarea>');

First copy and past the above code in to “PHP Execute” block. Second, specify the menus you like to export in the line 3 variable ‘$menus’
This script generates two arrays encapsulated in functions _menu_installed_items() and _menus_altered() that you will have to supply in your custom module installation file in next step

Part – II : Build Custom Module to Import Menus

Create a custom block with the following .install script:

function MODULE_NAME_install(){
    $t = get_t();

   // clear menus and create new ones if doesn't exist
    foreach(_menus_altered() as $menu){
        //remove menu links for clean install
        menu_delete_links($menu);
        drupal_set_message($t('Links deleted from menu - ' . $menu['menu_name']));

       if(!menu_load($menu['menu_name'])){
           //doesn't exist...lets create one
           drupal_set_message($t('Missing menu '.$menu['menu_name'].'...creating'));
           menu_save($menu);
       }
    }

    //map containing the structure of the menu
    $alias_map = array();

    //shuffle so that parents are always above the children menu items
    //this is important so when building the $alias_map child is not being inserted without parent already present
    $all_items = _menu_installed_items();
    $parents_p1 = array();
    $parents_p2 = array();
    $parents_p3 = array();
    $parents_p4 = array();
    $parents_p5 = array();

    foreach($all_items as $key => $item){
        if($key == $item['p1']){
            $parents_p1[$key] = $item;
        }
        if($key == $item['p2']){
            $parents_p2[$key] = $item;
        }
        if($key == $item['p3']){
            $parents_p3[$key] = $item;
        }
        if($key == $item['p4']){
            $parents_p4[$key] = $item;
        }
        if($key == $item['p5']){
            $parents_p5[$key] = $item;
        }
    }

    $sorted_menu_list = array_merge($parents_p1,$parents_p2,$parents_p3,$parents_p4,$parents_p5);

    //add links
    foreach ($sorted_menu_list as $key => $item) {
        $alias_link_path = $item['link_path'];
        //look up node based on the alias path of existing drupal instance
        $item['link_path'] = drupal_get_normal_path($alias_link_path);

        //drupal_set_message('plid: '.$item['plid']. ' alias_map:' . $alias_map[$item['plid']] );
        $item['plid'] = ($item['plid'] !== 0 && !empty($alias_map[$item['plid']])) ? $alias_map[$item['plid']] : $item['plid'] ;
        $item['p1'] = ($item['p1'] !== 0 && !empty($alias_map[$item['p1']])) ? $alias_map[$item['p1']] : $item['p1'] ;
        $item['p2'] = ($item['p2'] !== 0 && !empty($alias_map[$item['p2']]) ) ? $alias_map[$item['p2']] : $item['p2'] ;
        $item['p3'] = ($item['p3'] !== 0 && !empty($alias_map[$item['p3']])) ? $alias_map[$item['p3']] : $item['p3'] ;

        $installed = menu_link_save($item);
        if($installed !== FALSE) {
            drupal_set_message($t('Menu Item : '. $key . ' installed'));
            //drupal_set_message('INSERT: link_path: '.$alias_link_path. ' mlip:' . $installed );
            $alias_map[$alias_link_path] = $installed;
        }else{
            drupal_set_message($t('Menu Item : '. $key . ' was not installed'));
        }
    }
    menu_link_save($item);
    menu_cache_clear_all();
    drupal_set_message($t('Installed Menu'));
}

//ADD THE TWO FUNCTIONS FROM PART I HERE

Make sure you add the two functions generated in Part I (i.e._menus_altered(),_menu_installed_items() ) in the .install file where it says ‘//ADD THE TWO FUNCTIONS FROM PART I HERE’. These are used by the script to build the new menus

Thats all….in future we hope to automize the above process by creating module if the problem is not solved soon by the features,uuid modules.

If Custom Path Alias…

The features module currently does not export/import path_alias for nodes. This is okey if you are using path alias of the node that were generated by default by pathauto module, however. If not, then your custom alias path for nodes are not exported/imported via features and the above menu export/import solution will not work. We have created module to export/import path alias published at https://github.com/kapasoft-config-scripts/designssquare_alias_path. By enabling this module, your custom path alias is going to be exported and then imported for all nodes.

Troubleshooting

All of the menu items does not import

Make sure all the menu items imported are pointing to path alias that are unique. If not unique, then you have to break into more than one menu import to work.

How To Implement Custom Field Types In Drupal

In this post, we go over on how to create, handle and display a custom fields in Drupal. In addition, we look on situations when your custom field contains an artifacts that requires an additional care

The hook_field_schema() defines the columns. The table will be only added after an actual field is created(attached to content type). So, if you like to see if the data is being added for the custom field, look into table ‘field_xxx’

The hook_field_shema() needs to be define and declared in the module .install file as following:

function NAME-OF-SCHEMA_field_schema($field) {
    $schema = array();
    $schema['columns']['class'] = array(
        'type' => 'varchar',
        'length' => 50,
        'not null' => FALSE
    );
  
    $schema['columns']['image'] = array(
        'description' => 'The {file_managed}.fid being referenced in this field.',
        'type' => 'int',
        'not null' => FALSE,
        'unsigned' => TRUE,
    );

    $schema['foreign keys'] = array(
        'image_fid' => array(
            'table' => 'file_managed',
            'columns' => array('image' => 'fid'),
        ),
    );

    return $schema;
}

In the above example, a schema of 2 fields are created. One of the fields are a file upload, so there is additional foreign constrain defined.

Test from PHP Execute block

$field = field_info_field('field_designssquare_slider_layer');
module_load_install('field_designssquare_slider_layer');
$schema = (array) module_invoke('field_designssquare_slider_layer', 'field_schema', $field);

Look into Watchdog logs for a message. You should see the message if the install was performed

The columns can also be checked in the table ‘field_NAME_OF_FIELD’ in database

Specify Display

You may like to create a display format for the custom field. The field display gives user ability to select display for the field. Developer can take it and create separate layout via custom theme functions wrapped into template file

To declare display:

/**
 * Implements hook_field_formatter_info().
 */
function videojs_field_formatter_info() {
  return array(
    'videojs' => array(
      'label' => t('Video.js : HTML5 Video Player'),
      'field types' => array('file', 'media', 'link_field'),
      'description' => t('Display a video file as an HTML5-compatible with Flash-fallback video player.'),
      'settings'  => array(
        'width' => NULL,
        'height' => NULL,
        'posterimage_field' => NULL,
        'posterimage_style' => NULL,
      ),
    ),
  );
}

The ‘field types’ specify to which types the display is available. The ‘settings’ is custom parameter passed to the display(will cover later). So, this declares the display available for user to specify from UI. Next, lets implement the display:

/**
 * Implements hook_field_formatter_view().
 */
function videojs_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  if ($display['type'] !== 'videojs') {
    return array();
  }
  if (empty($items)) {
    return array();
  }

  if ($field['type'] == 'link_field') {
    //for field type of 'link_field' do things differently
  }

  $settings = $display['settings'];
  $attributes = array();
  if (!empty($settings['width']) && !empty($settings['height'])) {
    $attributes['width'] = intval($settings['width']);
    $attributes['height'] = intval($settings['height']);
  }


  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
  return array(
    array(
      '#theme' => 'videojs',
      '#items' => $items,
      '#player_id' => 'videojs-' . $id . '-' . str_replace('_', '-', $instance['field_name']),
      '#attached' => videojs_add(FALSE),
      '#entity' => $entity,
      '#entity_type' => $entity_type,
      '#attributes' => $attributes,
      '#posterimage_style' => !empty($settings['posterimage_style']) ? $settings['posterimage_style'] : NULL,
    ),
  );
}

The display formatter calls a custom function declared as following:

function videojs_theme() {
  return array(
    'videojs' => array(
      'variables' => array('items' => NULL, 'player_id' => NULL, 'attributes' => NULL, 'entity' => NULL, 'entity_type' => NULL, 'posterimage_style' => NULL),
      'template' => 'theme/videojs',
    ),
}

Here, the theme custom function used for rendering display is declared. This theme custom function uses a template file ‘theme/videojs’ to render output, but before that the hook_preprocess_CUSTOM-THEME-FUNC is called, so the variables can be preprocessed.

Render the Display

To render specified display as following:

$video_render_array = field_view_field(
                        'node',
                        $entity,
                        'field_video_file',
                        array(
                            'type' => 'videojs',
                            'label' => 'hidden',
                            'settings' => array(
                                  'width' => '300',
                                  'height' => '400',
                                  'posterimage_field' => NULL,
                                  'posterimage_style' => NULL,
                            ),
                        )
                    );
 $content = render($video_render_array);

Here the field ‘field_video_file’ uses a display ‘jw_player’ that takes one parameter jwplayer_preset. Afterwards, the renderable array returned from the display is being rendered.

Create Custom Field Type

To declare custom field type use hook_field_info() as following:

function MODULE-NAME_field_info() {
 return array(
        'OUR_CUSTOM_FIELD_NAME' => array(
            'label' => t('Slide Layer'),
            'description' => t('field containing info about one of the layers part of the Rev Slider'),
            'settings' => array('max_length' => 255),
            'instance_settings' => array(
                'text_processing' => 0,
            ),
            'default_widget' => 'OUR_CUSTOM_FIELD_NAME_widget',
            'default_formatter' => 'OUR_CUSTOM_FIELD_NAME_formatter',
        ),
    );
}

Besides name and description, the default widget and formatter is specified. Next, lets declare and implement the widget for this field type :

/**
 * Implements hook_field_widget_info().
 * Expose Field API widget types.
 */
function MODULE-NAME_field_widget_info() {
    return array(
        'OUR_CUSTOM_WIdGET_NAME' => array(
            'label' => t('Revolution Slide Layer'),
            'field types' => array('OUR_CUSTOM_FIELD_NAME'),
        ),
    );
}

The widget name is given and assigned to our custom field type – OUR_CUSTOM_FIELD_NAME via ‘field types’. After declaring the widget, lets specify the form for user to enter values:

/**
 * Implements hook_field_widget_form().
 */
function MODULE-NAME_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
    $element += array(
        '#type' => $instance['widget']['type'],
        '#default_value' => isset($items[$delta]) ? $items[$delta] : '',
    );
    return $element;
}

/**
 * Implements hook_element_info().
 * Declare the field Form API element types and specify their default values.
 */
function MODULE-NAME_element_info() {
    $elements = array();
    $elements['OUR_CUSTOM_WIdGET_NAME'] = array(
        '#input' => TRUE,
        '#process' => array('designssquare_rev_slider_layer_field_process')
    );
    return $elements;
}

In the function hook_field_widget_form(), it specifies the widget type, so we can use hook_element_info for specifying form. Afterwards, the hook_element_info() is used to specify function returning the renderable array with columns of the schema from the .install file

At last, the widget form is specified:

function designssquare_rev_slider_layer_field_process($element, $form_state, $complete_form) {
    $element['layer_header'] = array(
        'layer_header' => array(
            '#type' => 'markup',
            '#weight' => 0,
            '#markup' => '<p>' . t('Slide Layer '.($element['#delta'] + 1)) . '</p>',
        ),
    );
$element['image'] = array(
        '#title' => t('Image'),
        '#type' => 'managed_file',
        '#weight' => 8,
        '#description' => t('Upload a file, allowed extensions: jpg, jpeg, png, gif'),
        '#default_value' => isset($element['#value']['image']) ? $element['#value']['image'] : '',
        '#upload_location' => REV_SLIDER_DEST.'/img',
        '#states' => array(
            'visible' => array(
//                ':input[title="content_choice_'.$element['#delta'].'"]' => array('value' => 'image'),
                ':input[name="content_choice['.$element['#delta'].']"]' => array('value' => 'image'),
            ),
        ),
        '#upload_validators' => array(
            'file_validate_extensions' => array('jpg jpeg png gif'),
            // Pass the maximum file size in bytes
            'file_validate_size' => array(MAX_SIZE_LIMIT_DS*1024*1024),
        ),

    );
    // To prevent an extra required indicator, disable the required flag on the
    // base element since all the sub-fields are already required if desired.
    $element['#required'] = FALSE;

    return $element;
}

We still have to declare and implement hook_field_is_empty($item, $field), so it know when the field is considered to be empty

function MODULE-NAME_field_is_empty($item, $field)
{
    return !isset($item);
}

Here, we tell that empty is when no instance of our custom field is present

Validating Custom Field Values

To validate the custom field, we first register our custom validation function via hook_formt_alter():

function MODULE-NAME_form_alter(&$form, &$form_state, $form_id)
{
    switch ($form_id) {
        case 'OUR-FORM-ID':
            //assign validation
            $form['#validate'][] = 'some_custom_validate';
            //assign submition
            $form['#submit'][] = 'some_custom_submit';
            break;
    }
}

Since this is a general hook, we filter out only the form of our interest. (For finding Form ID, please, see post ‘Forms In Drupal Overview‘). Afterwards, we specify our custom function for validating the form

Next, the validation logic is declared:

function some_custom_validation(){
 if(empty($form_state['values']['some_field']['und'])){
         form_set_error('some_field[und]', t("error message comes here"));
}

On validation, our custom validation function is being called in which we perform validation for our custom field values

Handling Artifacts For Custom Field

If your custom field contains files, then there is extra care of keeping them permanent as well as removed on deletion. In the above section “Validating Custom Field Values”, we registered our submission processor function. By default all artifacts via file_manage is saved temporary. Next, we ensure the artifacts are permanent on creation and deleted on the remove event:

function some_custom_submit($form, &$form_state)
{
  
        if ((isset($form_state['clicked_button']) && $form_state['clicked_button']['#value'] == "Delete")) {
            //custom field needs to be deleted
            //remove artifact image
            $file = file_load(['values']['our_custom_field']['image']);
            if ($file) {
                file_delete($file);
                file_usage_delete($file, 'MODULE-NAME');
            }
        } else {
            //custom field needs to be added
            //make permanent image file
            $file = file_load(['values']['our_custom_field']['image']);
            if ($file) {
                // Change status to permanent.
                $file->status = FILE_STATUS_PERMANENT;
                //all permanent files needs an entry in the 'file_usage' table
                $id = (isset($form_state['node']->nid)) ? $form_state['node']->nid : 0;
                file_usage_add($file, 'MODULE-NAME', $form_state['node']->type, $id);
                // Save.
                file_save($file);
            }
        }
    }
}

First, we check to see if the node is being deleted or created. Afterwards, we load the artifact and make it permanent or remove it if being deleted

See post ‘Handling Assets For Custom Module In Drupal’ for how to automize artifact import/export

Handling Multiple Instances of Custom Field

In case, user chooses your custom field to be more than one time for specific node, then there is module field_remove_item that allows easy for them to remove any of the instance, however. You will have to handle the artifacts again. That easy to do via hook_field_validate()

function MODULE-NAME_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors)
{
    //remove artifacts  on 'remove item' bottom
            foreach ($items as $key => $item) {
               //see if any of the layers is selected to be deleted
                if ($item['field_remove_item']) {
                    //remove image
                    $file = file_load($item['image']);
                    if ($file) {
                        file_delete($file);
                        file_usage_delete($file, 'MODULE-NAME');
                    }
                }
            }
}

Here, we loop through each instance of your custom field since we don’t know exactly which of the one is being removed. The instance with field ‘field_remove_item’ set, is the one we have to remove artifacts, so we have if statement following with standard way of removing asset managed with manage_file

Uninstalling Custom Fields

If you developed a custom field and installed as a separate module than it will create a lock where you will not be able to uninstall the module. There are two solutions that i am aware. The quick and dirty is to update table ‘fields_config’ to remove the dependency. Another solution is to create a separate module for the purpose to run uninstall hook during which it removes the field, thus, removing the lock so it becomes available to be uninstalled. Lets look at the last solution

Here is the hook_uninstall() for uninstalling the custom module:

function MODULE-NAME_uninstall()
{
    //remove fields from content types
    foreach(_get_bundles_for_field_type('CUSTOM-FIELD-TYPE') as $bundle){
        field_attach_delete_bundle('node', $bundle);
        drupal_set_message('uninstalled field for bundle:'.$bundle);
    }

    //remove tables
    drupal_uninstall_schema('CUSTOM-FIELD-SCHEMA');

    // remove variables
    db_query("DELETE FROM {variable} WHERE name LIKE 'CUSTOM-FIELD-SCHEMA_%'");

    // Remove assets.
//    file_unmanaged_delete_recursive(file_default_scheme() . '://slider');

    // Clear the cache.
    field_info_cache_clear();
}

The function ‘_get_bundles_for_field_type’ returns all the bundles referencing the custom field. Afterwards, we use field_attach_delete_bundle API To remove our custom field type as well as all of its instances. Next the schema is removed, the variables deleted and files removed. At last, we clear the cache

Here is the implementation of _get_bundles_for_field_type():

//returns all the bundles of entity instances that references a field instance with a given type
function _get_bundles_for_field_type($field_type)
{
    $field_map = field_info_field_map();
    $bundles = array();
    foreach ($field_map as $field_name => $item) {
        if ($item['type'] == $field_type) {
            foreach ($item['bundles'] as $bundle_list) {
                foreach ($bundle_list as $bundle) {
                    $bundles[] = $bundle;
                }
            }
        }
    }
    return array_unique($bundles);
}

Render The Field

Troubleshooting

1. PDOException: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry ‘public://some/path/img.jpg’ for key ‘uri’: INSERT INTO {file_managed}

This issue came up when our custom field was a file and by reinstalling the field it created a duplicate entry in the manage_file table. The solution was clean file_manage table when instance with our custom field was delete

//clean file_manage database on field instance delete
function MODULE-NAME_node_delete($node){
    if($node->type == 'CERTAIN-TYPE'){
        //removes images from file_manage/file_usage tables
        $field_instance = field_get_items('node', $node, 'field_FIELED-NAME');
        foreach($field_instance as $key => $instance){
            $file = file_load($instance['image']);
            file_delete($file);
            file_usage_delete($file, 'MODULE-NAME');
  ...
}

The hook-node_delete($node) is called when any node is being deleted. Here, in line 5, we look up our custom fields for the node being deleted and then clean the file entry in file_manage and file_usage tables.

This also removes the images from Drupal public directory(by default sites/default/files). We did made files being copied when module is being enabled:

function MODULE-NAME_uuid_node_features_rebuild_alter(&$node, $module){
...
            $source = _get_source_file($final_file->uri);
            file_unmanaged_copy($source, $final_file->uri);
...
}

function _get_source_file($uri){
    $source = drupal_get_path('module','MODULE-NAME').'/imports/public/'.substr($uri, 9);
    if(!file_exists($source)){
        watchdog(WATCHDOG_NOTICE, 'MODULE-NAME Import: Image does not exists at '.$source);
    }
    return $source;
}

The hook_uuid_node_features_rebuild_alter is covered in the article Make Custom Field With File Exportable In Drupal Features. We just extend it with file transfer when module feature is being enabled. In highlighted line, we copy from predefined location to a Drupal public folder.

See also post “Handling Artifacts for Custom Module In Drupal” for solution on handling assets such as images, files

2. Drupal claim “Field type(s) in use”

This message is displayed when you would like to disable the module but Drupal doesn’t permit. To solve problem, go to ‘field_config’ table and update the ‘type’ and ‘module’ text for your custom field. More info on the issue https://drupal.org/node/1284380

Fields pending deletion

This comes up after the custom field is removed but the chrom job run only once which is not sufficient. It needs to run several times.

Reference:

  • http://clikfocus.com/blog/how-set-custom-field-type-using-drupal-7-fields-api
  • http://drewpull.drupalgardens.com/blog/drupal-7-field-api-sample
  • http://drupaltutorial4u.blogspot.com/2011/04/how-to-create-custom-field-for-drupal-7.html
  • https://drupal.org/project/examples
  • http://drewpull.drupalgardens.com/blog/drupal-7-custom-field-formatter
  • http://drupal.stackexchange.com/questions/49958/hook-field-schema-does-not-work
  • http://nodesforbreakfast.com/article/2012/02/24/create-custom-form-api-elements-hookelementinfo-drupal-7
  • https://api.drupal.org/api/drupal/includes%21database%21schema.inc/group/schemaapi/7
  • http://www.slideshare.net/zugec/fields-in-core-how-to-create-a-custom-field

So it is bit difficult to find the best one who can solve all

free articles online the most effective natural anti

Celine Outlet Jen decided that she wanted to help other people make money from home. So she compiled all of her steps in an easy to follow guide. This guide turned into the One Week Marketing step by step plan. I agree with this. Not to just let them cry until they drop, but to let a little time pass before responding. They need to know that you are there for them, but at the same time not at their beck and call. Celine Outlet

Cheap Celine Outlet For $28, with its mint coriander sauce, while a Fijian Tuna ($30) has the same mint coriander sauce. Lobster lovers will seek out the Whole Lobster in the Shell, a 2 lb crustacean at market price, usually about $25 a pound. Last night my lovely server, Aylya, told me that she had been named for an original character in Star Wars.. Cheap Celine Outlet

Celine Bags Replica As a homeowner; you should hire a professional Plumber who can provide all plumbing services, both repairs and installations. It is really very challenging to find out an expert Plumber in New Zealand as there are so many online companies who are providing plumbing services. So it is bit difficult to find the best one who can solve all plumbing problems safely. Celine Bags Replica

Celine Replica handbags Before throwing away your beauty products, get out every little bit. For a products that come in tubes, think about using a squeezer that is made for toothpaste. You can turn your bottles upside down or to their side to get out every little bit of content. Celine Replica handbags

Celine Bags Online George Zimmerman attempted to hold an auction to sell the gun he used to kill Trayvon Martin. EST with replica celine a starting bid of $5,000, but the gun has disappeared from the auction site without explanation. The George Zimmerman trial ended with a not guilty verdict in July 2013, but the former Florida neighborhood watch volunteer said http://www.perfectceline.com the Department of Justice only recently returned the weapon.. Celine Bags Online

Celine Replica The Field of Dream movie may use baseball as a vehicle, but the movie’s really about love, second chances, and the power of hope. Imagination, left untethered, can free the mind to dream and hope things never thought possible. And it’s a baseball movie! Why do you think I love it so much. Celine Replica

replica celine handbags It is second only to drug trafficking as the most profitable illegal industry in the world. Child trafficking generally involves transportation from one country to another, where legal entry would be denied upon arrival at the international border. There may be no deception involved in the agreement. replica celine handbags

Celine Replica Bags The US has a deep interest in the country due to its oil reserves. It is also showing keen interest in the currency and the President Obama has also offered to equalize the currency to the US dollar. The economists of Iraq are thinking deeply over the proposition and very soon they would come up to a good decision. Celine Replica Bags

replica celine purse If you are reading information from an e cig company who manufactures the vaping devices, the answer will be it’s safe. Scientists are vigorously researching to determine if vaping causes lung cancer similar to cigarette smoking. One study has indicated that vaping can alter the gene expression of the lung cells, but did not conclude that it would produce cancer replica celine purse.

The Brute: In the BF there’s Jujoji who has some devastating

Get nine each of their souls, plus the axes the Axe Armors are liable to drop (buy more from Hammer if you didn’t get 9 in the first place), make a bunch of Bhuj, and then sell them for an easy 90,000G. Repeat it a few times and you’ll have no trouble whatsoever footing the bill for that Soul Eater Ring. It even works great later in the game, too. Do Not Run with a Gun: If you’re running, you stop when https://www.beltsoutletses.com you use a weapon or a Bullet soul.

Replica Hermes Belt So basically everything except the music, the basic concepts, and Mario. Conspicuously Light Patch: The weather effects don’t work on Invisible Blocks. Dual Boss: Some levels end in two Boom Booms, or even three. One of the Desert Dares has four. Easy Level Trick: One of the hardest Desert Dares involves staying alive for a set amount of time against a bomb throwing Warkitu. To survive, you’ll need to do some serious running and jumping around to avoid the bombs, or you could simply not move at all, and watch in disbelief as all the bombs fail to make their target. Replica Hermes Belt

Replica Hermes Handbags Vogler really meant for the best for the world. The Brute: In the BF there’s Jujoji who has some devastating magic powers. The Fabulous Fitzgerald loves to carve up people with his snapping fingers. The Call Has Bad Reception: Dr. Vogler had good intentions. Originally, his Fake Hermes Belts son had good intentions. The only problem was that by the time Dr. Vogler tells his son what needed to be done, he didn’t have a very good vocabulary. Canon Immigrant: When Tetsujin 28 was remade in 2004, the Murasame Kenji that appears there isn’t the original characterization it’s this one. Replica Hermes Handbags

Hermes Replica Handbags Also an example of. Mugging the Monster: Rob Anders to John at the dance. Soon after, a more literal example in the drifter trying to Back Stab Mr. Crowley. Nightmare Fuel Station Attendant: John Nothing Exciting Ever Happens Here: The first murder is specifically said by the main character to be the most exciting thing he’s ever seen happen in Clayton County. Parental Substitute: John’s therapist, Ben Neblin, kinda acts as a replacement for John’s absent father. So does Mr. Hermes Replica Handbags

Replica Hermes Close is back with a new look, Lock is gone, and the new villains Stop and Freeze are taken out of silhouette. Consequently, the shots of them battling the Cures are changed to match. The new Dysdark castle can also be seen in the background of Scarlet and Flora fighting some Zetsuborgs (though technically the faint outline of it had been there through Episodes 26 31). The fairy Kuroro is added in Episode 34. Prince Kanata is back in the opening sequence as of Episode 41. Replica Hermes

Replica Hermes Bags Continuity Reboot: Billed as this by the creators. Colour Coded for Your Convenience: The Main Bronze Saints. Cool Mask: The Pope now wears one that looks like a combination of a skull and an Incan headress. The new motuh plated helmets give this feel too. Damsel in Distress: Saori, as per tradition. Considerably less so than in the original manga and anime, though. Death Equals Redemption: Mortally wounded, Gemini Saga has a sort of Heel Realization when Saori still tries to help him despite everything he’s done to her. Replica Hermes Bags

Replica Hermes Birkin Gonard is the main villain of series. Off the set, though, Lily is a mean individual who only cares about her needs. Mitzuki is kind to a fault and Gonard is. well he not evil, just Gonard. No Communities Were Harmed: Many of the backgrounds were based on actual locations in Japan like downtown Tokyo, as the most obvious example, and Yoyogi Park. Now Which One Was That Voice?: Type 3, the credits list the main voice cast along with their respective characters, but only list the names of guest voice actors and not whoever they played. Replica Hermes Birkin

Hermes Handbags Teens Are Monsters: The original group of “children” in the short story are actually in their teens when they kill the adults. Too Dumb to Live: Burt in the original short story. He dies. Town with a Dark Secret: Something wicked happened in Gatlin, and anybody over the age of 18 isn’t alive to tell the tale. Ultimate Evil: He Who Walks Behind The Rows. A later Stephen King book plays with the implication that his true identity is Randall Flagg, Stephen King’s meta villain Hermes Handbags.

Season 2 also gives us the Broken Man

Big Damn Heroes: Herbert, Startop, and even Trabb’s boy come to Pip’s rescue in the last act. Big Fancy House: Subverted. Satis House used to be one of these. Book Dumb: Joe Gargery. Boomerang Bigot: Pip becomes this after meeting Estella Break the Cutie: Compeyson did this to Miss Havisham, who does the same with Estella. Break the Haughty: Both Pip and Estella. Broken Aesop: A point of contention against the Revised Ending: a major point of Pip’s Character Development is learning he was never meant for Estella and that she is not the person he has idealized her to be.

Replica Hermes Meganekko: Rin. Even though she very much subverts the “timid” meganekko stereotype, when she is nice she can also be a genuinely sweet, if not downright cute person. Men Get Old, Women Get Replaced: Almost all recurring female characters being (or becoming) immortal. The lead characters Rin and Mimi are immortals and Replica Birkins Hermes remain at the same physical age throughout the series’ 65 year span, while their nameless female informants are replaced with their own younger apprentices after every Time Skip. Replica Hermes

Replica Hermes Bags Butt Monkey: In almost every episode, something goes wrong for Lindy. Call Back: In “Dance Fever,” Logan’s suit advertises Fireman Freddy’s Spaghetti Station. Cassandra Truth: Also in “Dance Fever”, when nobody believes Lindy as she repeatedly insists that Sherri is out to get her. They dismiss her frantic claims with the assumption that her (physical) fever is making her delirious, as Sherri is supposedly the nicest girl in school. But Lindy has already become wise to her true colors. Casual Danger Dialogue: In “Dear High School Self,” the gang is trapped in a garbage room and can’t escape. Replica Hermes Bags

Hermes Birkin Replica Asshole Victim: All the prison staff, Rhino and Warden Sharpe himself. Big Bad Ensemble: Charles “Charlie” Forsythe, an executed convict whose spirit poses a danger to anyone in the prison and Ethan Sharpe, the man responsible for sending Charlie to his undeserved death. Burn Baby Burn: Warden Sharpe burns all the inmates mattresses at one point to weed out Rabbitt’s supposed killer. Captivity Harmonica: And with it, a whole band of other instruments. Catapult Nightmare: The opening scene depicting Charlie’s execution is revealed to be a flashback dream that’s Sharpe is having when he wakes up from it by bolting upright and screaming. Hermes Birkin Replica

Hermes Replica Handbags Literary Agent Hypothesis: Wolfram by and large follows the narrative Chr de Troyes’ https://www.pursevalleyhermes.com unfinished Perceval li Gallois, but to justify where he deviates from it he claims that Chr got important details wrong. How does he know? Because he got the true account. This was written down by the wise Moorish astrologer Flegetanis, the son of an Arabian father and a Jewish mother, who read it from the stars. This led not a few scholars to set off in search of Wolfram’s alleged source, the book of Guiot, so far without success. Hermes Replica Handbags

Replica Hermes Belt Fully Absorbed Finale: The Companion Chronicles episodes “The Catalyst”, “Empathy Games” and “The Time Vampire” end Leela’s arc, which was set up in Gallifrey. Gallifrey, in turn, concludes Ace’s arc as a Time Lord Academy student, which was planned on TV during the Aborted Arc of the Cartmel Master Plan and explored further by Big Finish in the Lost Stories releases and Bernice Summerfield box sets. Future Me Scares Me: Romana with the Imperiatrix. Season 2 also gives us the Broken Man, whose plot is fuelled by this trope. Replica Hermes Belt

Replica Hermes Birkin A Load of Bull: Iron Will is a minotaur. Adam Smith Hates Your Guts: A non videogame version. The cost of tomatoes seems to increase every week. And one pony deliberately raises the price of a single cherry because Fluttershy was so desperate to buy one. She allowed the seller to gain a huge advantage in bargaining. The free market is not kind to the likes of the shy. And then, apparently just to be a Jerkass (or possibly because he thinks Fluttershy has confused herself to the point where she wouldn’t be able to actually pay his ridiculously inflated price) he sells the cherry for 2 bits to some other pony Replica Hermes Birkin.

Crouching Moron, Hidden Badass: Zeno appears to be this at

Bari sax, at that. Exact Words: See Oh, Crap!. Failure to Save Murder: Mr Jackson laments that he didn’t invent his FTL drive sooner. Designer Replica Handbags As alluded to in the explanatory section, Wade steps on a rake, sending him into a musical number, followed by a short trial sentencing him to 9999 years in prison. Once there, two inmates brag about their crimes. When Wade sheepishly admits his, the other two pull on the bars, terrified of sharing a cell with such a psychopath.

replica Purse After David’s initial hack alerts the Air Force to this problem they remove it, requiring David to use internal NORAD terminals to communicate with WOPR for the remainder of the movie. Explosive Instrumentation: While it doesn’t explain why it takes so long to cycle the game iterations of tic tac toe, the explosions are coming because Joshua’s overclocking WOPR, briefly running at higher potential, which its hardware can’t withstand. Failsafe Failure: Inverted. replica Purse

Screw the Rules, I’m Doing What’s Right!; Why Garmatha betrayed the angels and saved Ayase when Gambiel had them slaughter an entire village of humans to cover up one accidental death. Sequel Hook: The second half of the anime’s last episode is full of these. Some of the more important ones: Michael calls Ayase “Ayase oneesan”, and Ayase calls Michael by the name “Sakura”.

Replica Designer Handbags We rode that aluminum box up to mid station and changed to an even slower moving red chair. It was great. Ski resort has a mountain and a way to get up it, but it the details that make a resort great. Angered she had seen him Eros fled and the distraught Psyche roamed the earth trying in vain to find her lover. In the end Zeus took pity and reunited them, he also gave his consent for them to marry. There are variations of this legend but most have the same outcome.. Replica Designer Handbags

Fake Bags Ignored Expert: Dr. Avram Mandel, an expert in alien intelligence, mentions that he repeatedly said that the aliens were peaceful from the moment he was called by the government, but in the face of the obvious, he was ignored. He says this as he leaves for his home to spend what he thinks are his final hours with his family, which gave him some more time than the rest of the planet. Fake Bags

Replica Bags Damsel in Distress: One at the end of each level. They’re even labeled as such. The third game had three of them, each holding a crystal that was required to access the dungeon level back entrance to the king’s tower. The Vineans of Ixo can transport energy and oxygen on a light beam to their space city, built on a shattered star. However, to send the beam that far, they must first reflect it on a gigantic mirror made of ice. The mirror must be concave and flawless, as any imperfection will cause the beam to fire off course. Replica Bags

Wholesale replica bags And I Must Scream: In Cullen’s daydream from chapter 20, where he ends up in a mute, fancy version of his house with everybody he knows, but they all turn into zombies and Cullen can’t scream. Archangel Gabriel: Cabot keeps Gabriel kidnapped because he believes he is the human incarnation of the archangel of the same name. Break the Cutie: Benton Sage. Wholesale replica bags

Designer Replica Handbags (ii) Imposition of certain requirements on life insurance companies to promote prudent management. Such requirements include capital adequacy, as well as solvency of statutory funds. Obviously, these requirements would reduce the risk of companies going bankrupt and closing up with policyholders’ funds. Designer Replica Handbags

Replica Handbags Third, the societies that promoted war were also Crapsack Worlds; life was already a short and unpleasant hell anyway. Child mortality was high, plagues and famine were fairly regular, and losing a limb meant losing that precious scrap of food on the table, since there was no such thing as veteran support. Karma Houdinis roamed the streets while No Good Deed Goes Unpunished. Replica Handbags

Replica Wholesale Handbags Cover Innocent Eyes and Ears: Tae Woo and Han Dae cover Tae Yeon’s eyes when they witness Hak getting very close to Yona (to provide her with a cover story to hide her identity). Creepy Child: Mizari is rather young, likely no older than Yun, and is rather unsettling with his Slasher Smile and creepy eyes. Crouching Moron, Hidden Badass: Zeno appears to be this at first with seemingly careless attitude. Replica Wholesale Handbags

The thing is the landlord is going after the treasure so Dick can take a break for one episode. Justified because Dick is trying to get the treasure and the farm. If the good guys get the treasure, Dick won’t get anything other than a piece of the treasure, namely the amount needed https://www.replicasshandbags.com to pay the mortgage.

Fake Designer Bags Captain Geographic: Aces Abroad introduces a few, perhaps inevitably. Most recurring are the Living Gods, Egyptian Jokers who resemble the ancient gods. (They eventually get jobs in Vegas to escape persecution). Historical Badass Upgrade: Kreisau Circle was a small organization of aristocrats and intellectuals who opposed national socialism but limited their actions to sharing information with Allied intelligence agencies. They never planned or carried out any military or paramilitary operations. Hold the Line: After the Castle mission, you return to Isenstadt only to find that the Nazis have been building radio towers Fake Designer Bags.