Making It Permanent for File_Managed

The file_managed functionality comes with ajax capability to upload and remove file, however. While, it stores the file, it stores it temporarily,so unless we make it permanent, the files just uploaded will be removed after certain time. In this post, we demonstrate one of the many ways how to make the file upload via file_managed a permanent.

Hook Into File_Managed

First, we specify the callback for the form element ‘file_managed’ as following:

$form['backgrounds']['ds_theme_custom_background'] = array(
        '#title' => t('Custom Background'),
        '#type' => 'managed_file',
        '#description' => t('Upload a your custom background image, allowed extensions: jpg, jpeg, png, gif'),
        '#default_value' => isset($form_state['value']['ds_theme_custom_background']) ? $form_state['value']['ds_theme_custom_background'] : theme_get_setting('ds_theme_custom_background'),
        '#upload_location' => DESIGNSSQUARE_THEME.'/theme-settings',
        '#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_THEME*1024*1024),
        ),
        '#process' => array('our_custom_callback'),
    );
}

By specifying ‘#process’, the custom callback – our_custom_callback is being called when ‘upload’, ‘remove’ button is clicked

Make it Permanent

Next, we will ensure file is stored permanently by updating it status as following:

function our_custom_callback($element, &$form_state, $form){
    $element = file_managed_file_process($element, $form_state, $form);

    //file upload
    if (isset($form_state['input']['_triggering_element_name']) && $form_state['input']['_triggering_element_name'] == $element['upload_button']['#name'] && !empty($element['#file'])) {
        //make file permanent
        $file = $element['#file'];
        // Change status to permanent.
        $file->status = FILE_STATUS_PERMANENT;

        //all permanent files need an entry in the 'file_usage' table
        //we also add variable name as the 'type' parameter, so we can export via Sample Data module
        file_usage_add($file, 'designssquare_lib', $element['#name'], '1');
        // Save.
        file_save($file);
    }

    //file removed
    if (isset($form_state['input']['_triggering_element_name']) && $form_state['input']['_triggering_element_name'] == $element['remove_button']['#name']) {
        //ensure file is removed
        if(isset($element['#file']->fid) && $file = file_load($element['#file']->fid)){
            //file exist, lets remove
            //the file is removed despite the count of references present
            db_delete('file_managed')->condition('fid', $file->fid)->execute();
            db_delete('file_usage')->condition('fid', $file->fid)->execute();
            entity_get_controller('file')->resetCache();
        }
    }

    return $element;
}

Here, we check whichever button is clicked – ‘upload’ or ‘remove’. If it is ‘upload’, then we change the status of the file and add entry in the table ‘file_usage’ both needed to make file permanently stored. If button ‘remove’ is clicked, then we ensure the file is removed from both tables – file_managed as well as file_usage. The last step shouldn’t be needed, however. It happens that file_managed functionality, sometimes, adds more than one reference to the same file into file_manage table(see ‘count’) resulting into situation that it doesn’t remove the file because it sees more than one reference. So, we ensure the file is removed no matter how many references listed in the table.

Troubleshooting

1. Fatal error: Call to undefined function – our_custom_callback

For Drupal 2.26,2.28 and, probably, many other version, there seems to be a bug on how the form processing functions are called before all the dependencies are loaded resulting in the error “Fatal error: Call to undefined function – our_custom_callback”. To avoid it, we end up hooking into the Drupal bootstrap process to load our custom function before callback is called via hook_init() as following:

function MODULE-NAME_init(){
    //load for processing file_managed on Ajax calls(i.e. upload, remove)
    module_load_include('inc', 'MODULE-NAME', 'inc/file_defining_custom_callback');
}

Here, we load the file containing the definition of our callback – our_custom_callback. Since the hook_init is called before Drupal is executed, our custom callback is defined that way solving the above problem.

Adding Color Wheel to Form Element in Drupal

You may have input for choosing color. While there are many different ways to add color wheel, here we demonstrate one powered by Spectrum.js

1. Download Library and Customize Basic Palette

First download the Spectrum.js library(spectrum.js and spectrum.css) and then create basic color palette. Here is an example:

jQuery(".basic-color-palette").spectrum(
    {
        color: this.value,
        allowEmpty:true,
        showInput: true,
        className: "full-spectrum",
        showInitial: true,
        showPalette: true,
        showSelectionPalette: true,
        maxPaletteSize: 10,
        preferredFormat: "hex",
        localStorageKey: "spectrum.demo",
        move: function (color) {

        },
        show: function () {

        },
        beforeShow: function () {

        },
        hide: function () {

        },
        change: function() {

        },
        palette: [
            ["#000","#444","#666","#999","#ccc","#eee","#f3f3f3","#fff"],
            ["#f00","#f90","#ff0","#0f0","#0ff","#00f","#90f","#f0f"],
            ["#f4cccc","#fce5cd","#fff2cc","#d9ead3","#d0e0e3","#cfe2f3","#d9d2e9","#ead1dc"],
            ["#ea9999","#f9cb9c","#ffe599","#b6d7a8","#a2c4c9","#9fc5e8","#b4a7d6","#d5a6bd"],
            ["#e06666","#f6b26b","#ffd966","#93c47d","#76a5af","#6fa8dc","#8e7cc3","#c27ba0"],
            ["#c00","#e69138","#f1c232","#6aa84f","#45818e","#3d85c6","#674ea7","#a64d79"],
            ["#900","#b45f06","#bf9000","#38761d","#134f5c","#0b5394","#351c75","#741b47"],
            ["#600","#783f04","#7f6000","#274e13","#0c343d","#073763","#20124d","#4c1130"]
        ]
    }
);

So, we have three artifacts – spectrum.js, spectrum.css and basic-color-palette.js the last is the above code

2. Import Artifacts

Next, we attach these artifacts to the form. One way to do is as following:

$form['backgrounds'] = array(
        '#type' => 'fieldset',
        '#title' => t('Background'),
        '#group' => 'builder',
        '#attached' => array(
            'js' => array(
                drupal_get_path('module', 'MODULE-NAME') . '/js/color-picker/spectrum.js' => array(
                    'type' => 'file',
                    'scope' => 'header'
                ),
                drupal_get_path('module', 'MODULE-NAME') . '/js/color-picker/basic-palette.js' => array(
                    'type' => 'file',
                    'scope' => 'footer'
                ),
            ),
            'css' => array(
                drupal_get_path('module', 'MODULE-NAME') . '/js/color-picker/spectrum.css' => array(
                    'type' => 'file',
                    'scope' => 'header'
                ),
            ),
        ),
    );

Here, we have fieldset ‘background’ that will have input text field for the color(see next section). In this fieldset, we attach the three artifacts stored in some module – MODULE-NAME ‘js/color-picker’ directory. This will load the artifacts whenever we load the form for our color wheel

3. Attach the Wheel

At last, we attach the color wheel for particular form input field:

..
$form['backgrounds']['SOME-FORM-INPUT'] = array(
        '#type' => 'textfield',
        '#title' => t('Choose Background Color'),
        '#default_value' => isset($form_state['value']['SOME-FORM-INPUT']) ? $form_state['value']['SOME-FORM-INPUT'] : theme_get_setting('ds_theme_bg_color'),
        '#attributes' => array(
            'class' => array('basic-color-palette'),
        ),
    );

Here, we attach color wheel to the field – SOME-FORM-INPUT that is part of the field set – backgrounds. By adding class ‘basic-color-palette’ to this field, we attach the color wheel. Note, the class ‘basic-color-palette’ is the same element specified in the artifact basic-palette.js

Resources

http://bgrins.github.io/spectrum/