diff --git a/assets/css/dist/admin.css b/assets/css/dist/admin.css new file mode 100644 index 0000000..c390e33 --- /dev/null +++ b/assets/css/dist/admin.css @@ -0,0 +1,26 @@ +/* +---------------------------------------------------------------- + +admin.css +Gravity Forms Administration Styles +For the Signature Add-On +https://www.gravityforms.com + +Gravity Forms is a Rocketgenius project +copyright 2008-2023 Rocketgenius Inc. +https://www.rocketgenius.com +this may not be re-distributed without the +express written permission of the author. + +NOTE: DO NOT EDIT THIS FILE! +THIS FILE IS REPLACED DURING AUTO UPGRADE +AND ANY CHANGES MADE HERE WILL BE OVERWRITTEN. + +---------------------------------------------------------------- +*/ + +.gform-admin .gform-compact-view .gf_signature_container { + display: none; +} + +/*# sourceMappingURL=admin.css.map */ diff --git a/assets/css/dist/admin.min.css b/assets/css/dist/admin.min.css new file mode 100644 index 0000000..835d320 --- /dev/null +++ b/assets/css/dist/admin.min.css @@ -0,0 +1 @@ +.gform-admin .gform-compact-view .gf_signature_container{display:none} \ No newline at end of file diff --git a/assets/css/dist/theme.css b/assets/css/dist/theme.css new file mode 100644 index 0000000..8d04573 --- /dev/null +++ b/assets/css/dist/theme.css @@ -0,0 +1,65 @@ +/* +---------------------------------------------------------------- + +theme.css +Gravity Forms Common Theme Styles +For the Signature Add-On +A simple shared set of styles for form themes for the frontend. +https://www.gravityforms.com + +Gravity Forms is a Rocketgenius project +copyright 2008-2022 Rocketgenius Inc. +https://www.rocketgenius.com +this may not be re-distributed without the +express written permission of the author. + +NOTE: DO NOT EDIT THIS FILE! +THIS FILE IS REPLACED DURING AUTO UPGRADE +AND ANY CHANGES MADE HERE WILL BE OVERWRITTEN. + +---------------------------------------------------------------- +*/ + +.gfield_signature_container { + max-width: 100%; + position: relative; +} + +.gfield_signature_container canvas { + max-width: 100%; + } + +.gfield_signature_image { + max-width: 100%; + position: relative; +} + +.gfield_signature_image img { + max-width: 100%; + } + +.gfield_signature_reset_container svg.gfield_signature_reset_icon, .gfield_signature_reset_container svg.gform_signature_locked_reset, .gfield_signature_image svg.gfield_signature_reset_icon, .gfield_signature_image svg.gform_signature_locked_reset { + cursor: pointer; + opacity: 0.7; + position: absolute; + top: 5px; + } + +.gfield_signature_reset_container svg.gfield_signature_reset_icon:hover, .gfield_signature_reset_container svg.gfield_signature_reset_icon:focus, .gfield_signature_reset_container svg.gform_signature_locked_reset:hover, .gfield_signature_reset_container svg.gform_signature_locked_reset:focus, .gfield_signature_image svg.gfield_signature_reset_icon:hover, .gfield_signature_image svg.gfield_signature_reset_icon:focus, .gfield_signature_image svg.gform_signature_locked_reset:hover, .gfield_signature_image svg.gform_signature_locked_reset:focus { + opacity: 1; + } + +.gfield_signature_reset_container svg.gfield_signature_reset_icon, .gfield_signature_image svg.gfield_signature_reset_icon { + left: calc(100% - 29px); + } + +.gfield_signature_reset_container svg.gform_signature_locked_reset, .gfield_signature_image svg.gform_signature_locked_reset { + display: none; + left: 5px; + } + +.gfield_signature_image svg.gform_signature_locked_reset { + display: block; +} + +/*# sourceMappingURL=theme.css.map */ diff --git a/assets/css/dist/theme.min.css b/assets/css/dist/theme.min.css new file mode 100644 index 0000000..46ebcbc --- /dev/null +++ b/assets/css/dist/theme.min.css @@ -0,0 +1 @@ +.gfield_signature_container{max-width:100%;position:relative}.gfield_signature_container canvas{max-width:100%}.gfield_signature_image{max-width:100%;position:relative}.gfield_signature_image img{max-width:100%}.gfield_signature_image svg.gfield_signature_reset_icon,.gfield_signature_image svg.gform_signature_locked_reset,.gfield_signature_reset_container svg.gfield_signature_reset_icon,.gfield_signature_reset_container svg.gform_signature_locked_reset{cursor:pointer;opacity:.7;position:absolute;top:5px}.gfield_signature_image svg.gfield_signature_reset_icon:focus,.gfield_signature_image svg.gfield_signature_reset_icon:hover,.gfield_signature_image svg.gform_signature_locked_reset:focus,.gfield_signature_image svg.gform_signature_locked_reset:hover,.gfield_signature_reset_container svg.gfield_signature_reset_icon:focus,.gfield_signature_reset_container svg.gfield_signature_reset_icon:hover,.gfield_signature_reset_container svg.gform_signature_locked_reset:focus,.gfield_signature_reset_container svg.gform_signature_locked_reset:hover{opacity:1}.gfield_signature_image svg.gfield_signature_reset_icon,.gfield_signature_reset_container svg.gfield_signature_reset_icon{left:calc(100% - 29px)}.gfield_signature_image svg.gform_signature_locked_reset,.gfield_signature_reset_container svg.gform_signature_locked_reset{display:none;left:5px}.gfield_signature_image svg.gform_signature_locked_reset{display:block} \ No newline at end of file diff --git a/change_log.txt b/change_log.txt new file mode 100644 index 0000000..aad4ffb --- /dev/null +++ b/change_log.txt @@ -0,0 +1,189 @@ +### 4.6 | 2023-11-16 +- Updated the signature field to be compatible with Compact View in Gravity Forms 2.8. +- Fixed a PHP 8.1 notice on form submission. + +### 4.5 | 2023-06-21 +- Fixed an issue that makes the signature reset button hard to press using mobile browsers. + +### 4.4 | 2023-01-18 +- Updated styles to work with the Orbital theme in Gravity Forms 2.7. +- Updated the location of reset icons and lock icon to provide better accessibility and better user experience. +- Updated the editor icon. +- Fixed an issue on the entry edit page that prevents the signature from being edited. + + +### 4.3 | 2022-06-15 +- Fixed an issue where the signature field has no width when used in multiple forms on a page. + + +### 4.2 | 2022-02-17 +- Fixed an issue where the signature field has no width when other form fields use conditional logic. +- Fixed an issue where the signature field does not work correctly for mobile/touch devices. +- Fixed an issue where the lock reset button is not hidden when clicking the signature reset button when a signature is present and the screen is resized. +- Fixed an issue where a conditionally-hidden signature field's width is not updated or responsive. +- API: Updated SuperSignature to version 1.5.0.4. + + +### 4.1 | 2021-09-01 +- Fixed JavaScript errors on the edit entry page that prevent the signature field from being edited. +- Fixed an issue that causes the signature field to throw a JavaScript error on the front-end when the data input doesn't exist. +- Fixed an issue where script tags are output above document head element. +- Fixed a fatal error which can occur when saving the signature in some hosting environments. + + +### 4.0 | 2020-09-23 +- Added security enhancements. +- Added the [gform_signature_url](https://docs.gravityforms.com/gform_signature_url/), [gform_signature_url_permission_granted](https://docs.gravityforms.com/gform_signature_url_permission_granted/), and [gform_signature_url_require_login](https://docs.gravityforms.com/gform_signature_url_require_login/) filters. +- Added support for Gravity Forms 2.5 + + +### 3.9 | 2020-05-18 +- Added translations for Hebrew, Hindi, Japanese, and Turkish. +- Added the label visibility and description placement settings to the field appearance tab. +- Updated Javascript files to use minified versions. +- Fixed signing issue with Windows touch devices. + + +### 3.8 | 2019-07-31 +- Added a check to GFSignature::display_signature() to only use mime_content_type() if the signature image file is local. +- Added security enhancements. +- Added the *[gform_signature_delete_file_pre_delete_entry](https://docs.gravityforms.com/gform_signature_delete_file_pre_delete_entry/)* filter to disable deletion of the signature file or trigger deletion at a later time. + + +### 3.7 | 2019-03-19 +- Fixed signature field not appearing on single page forms or multi-page forms embedded without AJAX. +- Fixed signature field disappearing on multi-page forms embedded via AJAX when rotating mobile device. +- Fixed extra lines added to signature when signing with Chrome on Android. +- Fixed issue where signature was clipped when rotating on mobile devices. + + +### 3.6 | 2018-12-18 +- Added responsive support to Signature field. +- Fixed fatal error when displaying the signature image on some servers. +- Fixed PHP warning message. +- Updated SuperSignature to version 1.5.0.3 and used unhexed version. + + +### 3.5 | 2018-04-09 +- Added security fixes. +- Added query parameter to return signature as transparent image. +- Added GPL to plugin header. +- Updated Plugin URI and Author URI to use https. + + +### 3.4 | 2018-01-29 +- Added compatibility with Gravity Forms 2.3. +- Updated IeModalFix init option to only be set for IE browsers; fixes issue with FireFox with 3+ paths on a signature. +- Fixed a JavaScript error which could occur in the form editor with Gravity Forms 2.3-beta. +- Fixed an issue where the field could be populated with the value from another form on the same page if both fields have the same ID. +- Fixed an issue where the signature file could remain after the entry was permanently deleted. + + +### 3.3 | 2016-10-26 +- Updated initialization options for signature to set different values based on MAC or PC to fix issues with touch not working. + + +### 3.2 | 2016-10-26 +- Updated field appearance settings label classes for consistency with GF2.0+. +- Fixed issue where signing with more than three lines corrupted the signature. +- Fixed a fatal error if Html2RGB() has already been defined by the theme or another plugin. + + +### 3.1 | 2016-06-16 +- Added the [gform_signature_init_options](https://docs.gravityforms.com/gform_signature_init_options) filter. +- Updated to include the ss_orig.js script in the footer. +- Updated the signature container markup to use the height from the init options. +- Updated script to be initialized via *get_form_inline_script_on_page_render()* rather than inline with the input markup. +- Fixed an issue when signing on a Windows touch machine with chrome. +- Fixed an issue with the required field validation when updating an existing entry outside the entry detail context e.g. from a Gravity Flow user input step. +- Fixed a JavaScript error which could occur when the signature init script is included on a page and the field input is not e.g. the Gravity Flow user input step when the field is not editable. +- Fixed an issue with the border width style in the admin and on the front-end for old IE. +- Fixed a JavaScript issue which affected multi-page AJAX enabled forms when loading the next page. + + +### 3.0 | 2016-04-14 +- Added support for Gravity Forms 2.0. +- Added GF_Field_Signature. +- Updated SuperSignature to version 1.5.0.8. +- Fixed an issue where the old signature file was not deleted when using the 'sign again' link. +- Fixed an issue where the signature files would remain when permanently deleting all the entries for a form. +- Fixed an issue which prevented the signature canvas being displayed after deleting the existing signature when editing an entry. +- Fixed some strings in the form editor which were untranslatable. +- Fixed an issue with the borderStyle setting. +- Updated to only include the deleteSignature script on the entry detail edit page. +- Updated location of Signature field settings so they are now located on the Appearance tab. +- Updated SuperSignature; fixing a vertical offset issue with the touch point/capture location when the WP admin bar is present on a mobile device. +- Removed Browser.php. + + +### 2.4 | 2016-01-06 +- Added drag and drop support to the field button in the form editor (GF 1.9). +- Added Spanish (es_ES) translation. +- Updated POT file. +- Updated GFSignature::maybe_save_signature() to public. +- Updated signature script to latest version. +- Fixed a fatal error if Browser.php was already included by another plugin. +- Fixed an issue with the delete signature confirmation message. +- Fixed signature appearing as a black image in some email clients. +- Fixed issue a JavaScript error in the form editor in some languages. +- Fixed issue with causing mixed content warning when Force SSL was enabled in the admin. +- Fixed issue when multiple Html2RGB() functions were defined. +- Fixed issue with folder path causing signature not to be saved correctly on same servers. + + +### 2.3 | 2014-10-16 +- Added function gf_signature to easily get a Signature instance. +- Added text domain/path to header. +- Added support for save and continue in Gravity Forms 1.9. +- Added new filter: *gform_signature_show_in_all_fields* + add_filter( 'gform_signature_show_in_all_fields', '__return_false' ); +- Updated protected functions to be public. +- Updated to have the file class-gf-signature.php and organized code into this file and signature.php. +- Updated version to use a variable. +- Updated to display signature image in {all_fields} tag. + + +### 2.2 | 2014-04-14 +- Updated Super Signature script to latest to resolve issues with Kindle Fire HD. +- Fixed issue with IE11 when using the latest version of the Super Signature script. + + +### 2.1 | 2014-01-31 +- Downgraded super signature script to previous version due to an issue with IE11. + + +### 2.0 | 2014-01-29 +- Added Add-On Framework integration. +- Updated super signature script to 1.5.0.2. +- Updated masked input JS library to 1.3.1. +- Fixed issue creating signature image when output buffer wasn't empty. +- Fixed issue with signature being deleted when editing the entry. + + +### 1.3 | 2013-03-07 +- Fixed issue with signature file name being displayed on the print page instead of the signature image. +- Updated signature script to version 1.3.0.0. + + +### 1.2 | 2012-09-13 +- Added ability to add a signature in the entry detail page when a signature does not already exist. + + +### 1.1 | 2012-08-30 +- Fixed issue with custom validation message not getting displayed. +- Fixed issue with signature URL not being created correctly when resending notifications. +- Fixed conflict with IE8. +- Fixed issues when signing on IE9. +- Fixed issue where editing entry resulted in removal of signature from lead. +- Updated edit entry functionality to support previewing, downloading and deleting signature from lead. + + +### 1.0 | 2012-07-02 +- Fixed issue when signing on IE 9. +- Fixed issue with jQuery being included in the ss_orig.js file. +- Added support for ManageWP. +- Fixed issue with signature files not being deleted when lead is deleted. +- Fixed duplicate field size setting. +- Updated field size setting to an open text input instead of a drop down. +- Fixed conflict with datepicker field. +- Fixed issue with multi-page forms where signature was not maintaining its value from page to page. diff --git a/class-gf-signature.php b/class-gf-signature.php new file mode 100644 index 0000000..49fcaa6 --- /dev/null +++ b/class-gf-signature.php @@ -0,0 +1,778 @@ +is_gravityforms_supported() && class_exists( 'GF_Field' ) ) { + require_once 'includes/class-gf-signature-image.php'; + require_once 'includes/class-gf-field-signature.php'; + add_action( 'parse_request', array( $this, 'display_signature' ) ); + } + } + + /** + * Handles hooks and loading of language files. + */ + public function init() { + parent::init(); + + add_filter( 'gform_merge_tag_filter', array( $this, 'merge_tag_filter' ), 10, 5 ); + add_action( 'gform_delete_lead', array( $this, 'delete_entry' ) ); + add_action( 'gform_delete_entries', array( $this, 'delete_entries' ), 10, 2 ); + add_action( 'gravityforms_cron', array( $this, 'add_index_file' ) ); + + } + + /** + * Initialize the admin specific hooks. + */ + public function init_admin() { + parent::init_admin(); + + add_filter( 'gform_tooltips', array( $this, 'tooltips' ) ); + add_action( 'gform_field_appearance_settings', array( $this, 'field_settings' ), 10, 2 ); + } + + /** + * Initialize the AJAX hooks. + */ + public function init_ajax() { + parent::init_ajax(); + + add_action( 'wp_ajax_gf_delete_signature', array( $this, 'ajax_delete_signature' ) ); + } + + // # SCRIPTS & STYLES ----------------------------------------------------------------------------------------------- + + /** + * Return the plugin's icon for the plugin/form settings menu. + * + * @since 3.9.1 + * + * @return string + */ + public function get_menu_icon() { + + return file_get_contents( $this->get_base_path() . '/images/menu-icon.svg' ); + + } + + /** + * Enqueue styles. + * + * @since 4.4 + * @return array $styles + */ + public function styles() { + + $base_url = $this->get_base_url(); + $min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG || isset( $_GET['gform_debug'] ) ? '' : '.min'; + + $styles = array( + array( + 'handle' => 'gform_signature_editor_css', + 'src' => $base_url . "/assets/css/dist/admin{$min}.css", + 'version' => $this->_version, + 'enqueue' => array( + array( 'admin_page' => array( 'form_editor' ) ), + ) + ), + array( + 'handle' => 'gform_signature_foundation', + 'src' => $base_url . "/assets/css/dist/theme{$min}.css", + 'version' => $this->_version, + 'enqueue' => array( + array( $this, 'should_enqueue_frontend_script' ), + ), + ), + ); + + return array_merge( parent::styles(), $styles ); + } + + + /** + * Return the scripts which should be enqueued. + * + * @return array + */ + public function scripts() { + + $min = defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG || isset( $_GET['gform_debug'] ) ? '' : '.min'; + $form = $this->get_current_form(); + + $scripts = array( + array( + 'handle' => 'gform_masked_input', + 'enqueue' => array( + array( 'admin_page' => array( 'form_editor' ) ), + ) + ), + array( + 'handle' => 'super_signature_script', + 'src' => $this->get_base_url() . '/includes/super_signature/ss.js', + 'version' => $this->_version, + 'deps' => array( 'jquery' ), + 'in_footer' => true, + 'enqueue' => array( + array( $this, 'should_enqueue_frontend_script' ), + ), + ), + array( + 'handle' => 'super_signature_base64', + 'src' => $this->get_base_url() . '/includes/super_signature/base64.js', + 'version' => $this->get_version(), + 'enqueue' => array( + array( $this, 'should_enqueue_frontend_script' ), + ), + ), + array( + 'handle' => 'gform_signature_frontend', + 'src' => $this->get_base_url() . "/js/frontend{$min}.js", + 'version' => $this->get_version(), + 'deps' => array( 'jquery', 'super_signature_script', 'super_signature_base64' ), + 'in_footer' => true, + 'enqueue' => array( + array( $this, 'should_enqueue_frontend_script' ), + ), + 'strings' => array( + 'lockedReset' => wp_strip_all_tags( __( 'Reset to re-sign.', 'gravityformssignature' ) ), + ), + ), + array( + 'handle' => 'gform_signature_delete_signature', + 'src' => $this->get_base_url() . "/js/gfsignature_delete_signature{$min}.js", + 'version' => $this->get_version(), + 'deps' => array( 'jquery' ), + 'in_footer' => true, + 'enqueue' => array( + array( $this, 'should_enqueue_delete_script' ), + ), + 'strings' => array( + 'confirm_delete' => esc_html__( "Would you like to delete this file? 'Cancel' to stop. 'OK' to delete", 'gravityformssignature' ), + 'delete_nonce' => wp_create_nonce( 'gf_delete_signature' ), + 'form_id' => ! empty( $form['id'] ) ? absint( $form['id'] ) : false, + ), + ), + ); + + return array_merge( parent::scripts(), $scripts ); + } + + /** + * Frontend scripts should only be enqueued if we're not on a GF admin page and the form contains our field type. + * + * @since 4.4 + * + * @param array $form The current form object. + * + * @return bool + */ + public function should_enqueue_frontend_script( $form ) { + + $is_frontend = GFForms::get_page() === false; + $has_signature_field = ! rgempty( GFFormsModel::get_fields_by_type( $form, array( 'signature' ) ) ); + + return ( $is_frontend || $this->is_entry_edit() ) && $has_signature_field; + } + + /** + * Delete signature script should only be enqueued on the entry edit page on forms with a Signature field. + * + * @since 4.4 + * + * @param array $form The current form object. + * + * @return bool + */ + public function should_enqueue_delete_script( $form ) { + + $has_signature_field = ! rgempty( GFFormsModel::get_fields_by_type( $form, array( 'signature' ) ) ); + + return $this->is_entry_edit() && $has_signature_field; + } + + // # FIELD SETTINGS ------------------------------------------------------------------------------------------------- + + /** + * Add the tooltips for the field. + * + * @param array $tooltips An associative array of tooltips where the key is the tooltip name and the value is the tooltip. + * + * @return array + */ + public function tooltips( $tooltips ) { + $signature_tooltips = array( + 'signature_background_color' => '
' . esc_html__( 'Background Color', 'gravityformssignature' ) . '
' . esc_html__( 'Select the color to be used for the background of the signature area.', 'gravityformssignature' ), + 'signature_border_color' => '
' . esc_html__( 'Border Color', 'gravityformssignature' ) . '
' . esc_html__( 'Select the color to be used for the border around the signature area.', 'gravityformssignature' ), + 'signature_pen_color' => '
' . esc_html__( 'Pen Color', 'gravityformssignature' ) . '
' . esc_html__( 'Select the color of the pen to be used for the signature.', 'gravityformssignature' ), + 'signature_box_width' => '
' . esc_html__( 'Width', 'gravityformssignature' ) . '
' . esc_html__( 'Enter the width for the signature area in pixels.', 'gravityformssignature' ), + 'signature_border_style' => '
' . esc_html__( 'Border Style', 'gravityformssignature' ) . '
' . esc_html__( 'Select the border style to be used around the signature area.', 'gravityformssignature' ), + 'signature_pen_size' => '
' . esc_html__( 'Pen Size', 'gravityformssignature' ) . '
' . esc_html__( 'Select the width of the pen to be used for the signature.', 'gravityformssignature' ), + 'signature_border_width' => '
' . esc_html__( 'Border Width', 'gravityformssignature' ) . '
' . esc_html__( 'Select the border width to be used around the signature area.', 'gravityformssignature' ), + 'signature_message' => '
' . esc_html__( 'Message', 'gravityformssignature' ) . '
' . esc_html__( "Write the message you would like to be sent. You can insert fields submitted by the user by selecting them from the 'Insert Variable' drop down.", 'gravityformssignature' ) + ); + + return array_merge( $tooltips, $signature_tooltips ); + } + + /** + * Add the custom settings for the Signature field to the Appearance tab. + * + * @param int $position The position the settings should be located at. + * @param int $form_id The ID of the form currently being edited. + */ + public function field_settings( $position, $form_id ) { + + if ( $position == 0 ) { + ?> +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • +
  • + + +
  • + +
  • + + +
  • +
  • + + +
  • +
  • + + px +
  • + + get_value_entry_detail(). + * + * @return string + */ + public function merge_tag_filter( $value, $merge_tag, $options, $field, $raw_field_value ) { + + if ( $merge_tag == 'all_fields' && $field->type == 'signature' ) { + + $show_in_all_fields = apply_filters( 'gform_signature_show_in_all_fields', true, $field, $options, $value ); + if ( ! $show_in_all_fields ) { + return $raw_field_value; + } + + } + + return $value; + } + + // # HELPERS ------------------------------------------------------------------------------------------------------- + + /** + * Returns the URL for the specified signature. + * + * @deprecated 4.0 + * + * @param string $filename The filename for this signature. + * + * @return string + */ + public function get_signature_url( $filename ) { + _deprecated_function( __METHOD__, '3.10', 'GF_Signature_Url::get_url()' ); + + $path_info = pathinfo( $filename ); + $filename = $path_info['filename']; + + return site_url() . "?page=gf_signature&signature={$filename}"; + } + + /** + * Display the signature on it's own page. + * + * @param WP $wp The current WordPress instance. + */ + public function display_signature( $wp ) { + $data = $this->get_query_data(); + + if ( empty( $data ) ) { + return; + } + + $signature = new GF_Signature_Image( $this, $data['filename'], $data['form_id'], $data['field_id'], $data['transparent'], $data['download'], $data['hash'] ); + $signature->maybe_output(); + } + + /** + * Returns the string to be used as the primary query var in the signature URL. + * + * @since 4.0 + * + * @return string + */ + public function get_query_var() { + return 'gf-signature'; + } + + /** + * Gets data from the query string which identifies the signature to be output. + * + * @since 4.0 + * + * @return array + */ + public function get_query_data() { + if ( empty( $_GET ) ) { + return array(); + } + + $filename = $this->get_legacy_filename(); + $transparent = rgget( 't' ) === '1'; + + if ( ! empty( $filename ) ) { + return array( + 'filename' => $filename, + 'transparent' => $transparent, + 'hash' => '', + 'form_id' => 0, + 'field_id' => 0, + 'download' => false, + ); + } + + $data = array( + 'filename' => rgget( $this->get_query_var() ), + 'transparent' => $transparent, + 'hash' => rgget( 'hash' ), + 'form_id' => absint( rgget( 'form-id' ) ), + 'field_id' => absint( rgget( 'field-id' ) ), + 'download' => rgget( 'dl' ) === '1', + ); + + return $this->has_required_query_data( $data ) ? $data : array(); + } + + /** + * Determines if the required query arguments have values. + * + * @since 4.0 + * + * @param array $data The data retrieved from the query string. + * + * @return bool + */ + private function has_required_query_data( $data ) { + if ( empty( $data['filename'] ) ) { + return false; + } + + if ( empty( $data['hash'] ) ) { + $this->log_debug( __METHOD__ . "(): Aborting ({$data['filename']}); empty hash." ); + + return false; + } + + if ( empty( $data['form_id'] ) ) { + $this->log_debug( __METHOD__ . "(): Aborting ({$data['filename']}); invalid form-id." ); + + return false; + } + + if ( empty( $data['field_id'] ) ) { + $this->log_debug( __METHOD__ . "(): Aborting ({$data['filename']}); invalid field-id." ); + + return false; + } + + return true; + } + + /** + * Returns the filename from the legacy query string. + * + * @since 4.0 + * + * @return string|null + */ + public function get_legacy_filename() { + if ( rgget( 'page' ) !== 'gf_signature' ) { + return ''; + } + + return rgget( 'signature' ); + } + + + /** + * Maybe save the signature. + * + * @param string $input_name The input name to use when accessing the $_POST. + * @param string $name_prefix The text to use as the filename prefix. + * + * @return string + */ + public function save_signature( $input_name, $name_prefix = '' ) { + require_once( $this->get_base_path() . '/includes/super_signature/license.php' ); + if ( ! function_exists( 'GetSignatureImage' ) ) { + $this->log_error( __METHOD__ . '(): Aborting; GetSignatureImage() does not exist.' ); + + return ''; + } + + $signature_data = rgpost( $input_name ); + + // Remove undefined parameters. + $signature_data = base64_decode( $signature_data ); + $signature_data = str_replace( 'undefined', '', $signature_data ); + $signature_data = base64_encode( $signature_data ); + + $image = GetSignatureImage( $signature_data ); + if ( ! $image ) { + $this->log_error( __METHOD__ . '(): Aborting; unable to create image from signature data.' ); + + return ''; + } + + $folder = $this->get_signatures_folder(); + + if ( ! wp_mkdir_p( $folder ) ) { + $this->log_error( __METHOD__ . '(): Aborting; unable to create folder.' ); + + return ''; + } + + // Add index.html to prevent directory browsing. + $this->add_index_file(); + + $filename = $name_prefix . uniqid( '', true ) . '.png'; + $path = $folder . $filename; + $result = imagepng( $image, $path, 4 ); + imagedestroy( $image ); + + if ( ! $result ) { + $this->log_error( __METHOD__ . '(): Aborting; unable to save file.' ); + + return ''; + } + + return $filename; + } + + /** + * Used by the gform_delete_entry hook to delete any signatures for the entry currently being deleted. + * + * @param integer $lead_id The ID of the current entry. + */ + public function delete_entry( $lead_id ) { + + $lead = RGFormsModel::get_lead( $lead_id ); + $form = RGFormsModel::get_form_meta( $lead['form_id'] ); + + if ( ! is_array( $form['fields'] ) ) { + return; + } + + foreach ( $form['fields'] as $field ) { + if ( $field->type == 'signature' ) { + /** + * Enables the ability to disable deletion of the signature file or trigger deletion at a later time. + * + * @since 3.7.1 + * + * @param bool $delete_file Defaults to true. + * @param array $form The Form object. + * @param integer $lead_id The ID of the current entry. + * @param integer $field_id The ID of the current field. + */ + $delete_signature = apply_filters( 'gform_signature_delete_file_pre_delete_entry', true, $form, $lead_id, $field->id ); + if ( $delete_signature ) { + $this->delete_signature( $lead, $field->id ); + } + } + } + + } + + /** + * Used by the gform_delete_entries hook to delete any signatures for the entries currently being deleted. + * + * @param int $form_id The ID of the form for which the entries are being deleted. + */ + public function delete_entries( $form_id, $status ) { + + $form = RGFormsModel::get_form_meta( $form_id, $status ); + $signature_fields = GFAPI::get_fields_by_type( $form, 'signature' ); + + if ( ! empty( $signature_fields ) ) { + global $wpdb; + + foreach ( $signature_fields as $field ) { + + $input_id_min = (float) $field->id - 0.0001; + $input_id_max = (float) $field->id + 0.0001; + + + $status_filter = ''; + if ( ! empty( $status ) ) { + $status_filter = $wpdb->prepare( ' AND status=%s', $status ); + } + + if ( version_compare( self::get_gravityforms_db_version(), '2.3-dev-1', '<' ) ) { + $lead_details_table_name = GFFormsModel::get_lead_details_table_name(); + $lead_table_name = GFFormsModel::get_lead_table_name(); + + $filenames = $wpdb->get_col( $wpdb->prepare( "SELECT ld.value FROM {$lead_details_table_name} ld + INNER JOIN {$lead_table_name} l ON l.id = ld.lead_id + WHERE ld.form_id=%d AND ld.field_number BETWEEN %s AND %s {$status_filter}", $form_id, $input_id_min, $input_id_max ) ); + + } else { + $entry_meta_table_name = GFFormsModel::get_entry_meta_table_name(); + + $filenames = $wpdb->get_col( $wpdb->prepare( "SELECT meta_value FROM {$entry_meta_table_name} em + INNER JOIN {$wpdb->prefix}gf_entry e ON e.id = em.entry_id + WHERE em.form_id=%d AND em.meta_key=%s {$status_filter}", $form_id, $field->id ) ); + } + + if ( is_array( $filenames ) ) { + foreach ( $filenames as $filename ) { + $this->delete_signature_file( $filename ); + } + } + } + } + } + + /** + * Handler for the gf_delete_signature AJAX request. + */ + public function ajax_delete_signature() { + + check_ajax_referer( 'gf_delete_signature', 'gf_delete_signature' ); + + $lead_id = intval( $_POST['lead_id'] ); + $field_id = intval( $_POST['field_id'] ); + + if ( ! $this->delete_signature( $lead_id, $field_id ) ) { + esc_html_e( 'There was an issue deleting this signature.', 'gravityformssignature' ); + } + + die(); + } + + /** + * Initiates deletion of the signature file and updates the entry to remove the filename. + * + * @param integer|array $lead_id The ID of the current entry, or the full Entry object. + * @param integer $field_id The ID of the current field. + * + * @return bool + */ + public function delete_signature( $lead_id, $field_id ) { + + $lead = is_array( $lead_id ) ? $lead_id : RGFormsModel::get_lead( $lead_id ); + + $this->delete_signature_file( rgar( $lead, $field_id ) ); + + return GFAPI::update_entry_field( $lead['id'], $field_id, '' ); + } + + /** + * Deletes the signature file from the uploads directory. + * + * @param string $filename The signature filename. + */ + public function delete_signature_file( $filename ) { + + $folder = $this->get_signatures_folder(); + $file_path = $folder . $filename; + + //Prevent files from being deleted from folders other than the signature upload folder + $is_valid_dir = trailingslashit( dirname( $file_path ) ) == $folder; + + if ( file_exists( $file_path ) && $is_valid_dir ) { + unlink( $file_path ); + } + + } + + /** + * Checks HTTP_USER_AGENT for Internet Explorer + * + * @since unknown + * + * @return int + */ + public function is_ie() { + return preg_match( '/MSIE|Internet Explorer|Trident|Edge/i', getenv( 'HTTP_USER_AGENT' ) ); + } + + /** + * Returns the current database version of Gravtiy Forms. + * + * @since 3.4 + * + * @return string + */ + public static function get_gravityforms_db_version() { + + if ( method_exists( 'GFFormsModel', 'get_database_version' ) ) { + $db_version = GFFormsModel::get_database_version(); + } else { + $db_version = GFForms::$version; + } + + return $db_version; + } + + /** + * Add index file to signatures folder. + * + * @since 3.4.2 + * @access public + * + * @uses GFCommon::recursive_add_index_file() + * @uses GFSignature::get_signatures_folder() + */ + public function add_index_file() { + + // Get folder path. + $folder = $this->get_signatures_folder(); + + GFCommon::recursive_add_index_file( $folder ); + + } + + /** + * Get path to signatures folder. + * + * @since 3.4.2 + * @access public + * + * @uses GFFormsModel::get_upload_root() + * + * @return string + */ + public static function get_signatures_folder() { + + return GFFormsModel::get_upload_root() . 'signatures/'; + + } + +} diff --git a/images/menu-icon.svg b/images/menu-icon.svg new file mode 100644 index 0000000..ff90fb0 --- /dev/null +++ b/images/menu-icon.svg @@ -0,0 +1 @@ +signature icon \ No newline at end of file diff --git a/includes/class-gf-field-signature.php b/includes/class-gf-field-signature.php new file mode 100644 index 0000000..2e1d9e5 --- /dev/null +++ b/includes/class-gf-field-signature.php @@ -0,0 +1,575 @@ +is_gravityforms_supported( '2.5-beta-4' ) ? 'gform-icon--signature' : gf_signature()->get_base_url() . '/images/menu-icon.svg'; + } + + /** + * Returns the field's form editor description. + * + * @since 3.9.1 + * + * @return string + */ + public function get_form_editor_field_description() { + return esc_attr__( 'Allows users to sign online using a mouse or stylus.', 'gravityformssignature' ); + } + + /** + * Assign the Signature button to the Advanced Fields group. + * + * @return array + */ + public function get_form_editor_button() { + return array( + 'group' => 'advanced_fields', + 'text' => $this->get_form_editor_field_title() + ); + } + + /** + * Return the settings which should be available on the field in the form editor. + * + * @return array + */ + function get_form_editor_field_settings() { + return array( + 'pen_size_setting', + 'border_width_setting', + 'border_style_setting', + 'box_width_setting', + 'pen_color_setting', + 'border_color_setting', + 'background_color_setting', + 'conditional_logic_field_setting', + 'error_message_setting', + 'label_setting', + 'label_placement_setting', + 'admin_label_setting', + 'rules_setting', + 'visibility_setting', + 'description_setting', + 'css_class_setting' + ); + } + + /** + * Returns the scripts to be included for this field type in the form editor. + * + * @return string + */ + public function get_form_editor_inline_script_on_page_render() { + + // set the default field label + $script = sprintf( "function SetDefaultValues_signature(field) {field.label = '%s';}", $this->get_form_editor_field_title() ) . PHP_EOL; + + // initialize the fields custom settings + $script .= "jQuery(document).bind('gform_load_field_settings', function (event, field, form) {" . + + "var backColor = field.backgroundColor == undefined ? '' : field.backgroundColor;" . + "jQuery('#field_signature_background_color').val(backColor);" . + "SetColorPickerColor('field_signature_background_color', backColor);" . + + "var borderColor = field.borderColor == undefined ? '' : field.borderColor;" . + "jQuery('#field_signature_border_color').val(borderColor);" . + "SetColorPickerColor('field_signature_border_color', borderColor);" . + + "var penColor = field.penColor == undefined ? '' : field.penColor;" . + "jQuery('#field_signature_pen_color').val(penColor);" . + "SetColorPickerColor('field_signature_pen_color', penColor);" . + + "var boxWidth = field.boxWidth == undefined || field.boxWidth.trim().length == 0 ? '300' : field.boxWidth;" . + "jQuery('#field_signature_box_width').val(boxWidth);" . + + "var borderStyle = field.borderStyle == undefined ? '' : field.borderStyle.toLowerCase();" . + "jQuery('#field_signature_border_style').val(borderStyle);" . + + "var borderWidth = field.borderWidth == undefined ? '' : field.borderWidth;" . + "jQuery('#field_signature_border_width').val(borderWidth);" . + + "var penSize = field.penSize == undefined ? '' : field.penSize;" . + "jQuery('#field_signature_pen_size').val(penSize);" . + + "});" . PHP_EOL; + + // initialize the input mask for the width setting + $script .= "jQuery(document).ready(function () {jQuery('#field_signature_box_width').mask('?9999', {placeholder: ' '});});" . PHP_EOL; + + // saving the backgroundColor property and updating the UI to match + $script .= "function SetSignatureBackColor(color) {SetFieldProperty('backgroundColor', color);jQuery('.field_selected .gf_signature_container').css('background-color', color);}" . PHP_EOL; + + // saving the borderColor property and updating the UI to match + $script .= "function SetSignatureBorderColor(color) {SetFieldProperty('borderColor', color);jQuery('.field_selected .gf_signature_container').css('border-color', color);}" . PHP_EOL; + + // saving the penColor property + $script .= "function SetSignaturePenColor(color) {SetFieldProperty('penColor', color);}" . PHP_EOL; + + // saving the boxWidth property + $script .= "function SetSignatureBoxWidth(size) {SetFieldProperty('boxWidth', size);}" . PHP_EOL; + + // saving the borderStyle property and updating the UI to match + $script .= "function SetSignatureBorderStyle(style) {SetFieldProperty('borderStyle', style);jQuery('.field_selected .gf_signature_container').css('border-style', style);}" . PHP_EOL; + + // saving the borderWidth property and updating the UI to match + $script .= "function SetSignatureBorderWidth(size) {SetFieldProperty('borderWidth', size);jQuery('.field_selected .gf_signature_container').css('border-width', size + 'px');}" . PHP_EOL; + + // saving the penSize property + $script .= "function SetSignaturePenSize(size) {SetFieldProperty('penSize', size);}"; + + return $script; + } + + /** + * Returns the field inner markup. + * + * @param array $form The Form Object currently being processed. + * @param string|array $value The field value. From default/dynamic population, $_POST, or a resumed incomplete submission. + * @param null|array $entry Null or the Entry Object currently being edited. + * + * @return string + */ + public function get_field_input( $form, $value = '', $entry = null ) { + $is_entry_detail = $this->is_entry_detail(); + $is_form_editor = $this->is_form_editor(); + $is_block_editor = $this->is_block_editor(); + $is_admin = $is_entry_detail || $is_form_editor; + + $form_id = absint( $form['id'] ); + $id = $this->id; + $field_id = $form_id == 0 ? "input_$id" : 'input_' . $form_id . "_$id"; + + $init_options = $this->get_supersignature_init_options( $field_id, $form ); + + $bgcolor = rgar( $init_options, 'BackColor', '#FFFFFF' ); + $bordercolor = rgar( $init_options, 'BorderColor', '#DDDDDD' ); + $boxheight = rgar( $init_options, 'SignHeight', '180' ); + $boxwidth = rgar( $init_options, 'SignWidth', '300' ); + $borderstyle = rgar( $init_options, 'BorderStyle', 'Dashed' ); + $borderwidth = rgar( $init_options, 'BorderWidth', '2px' ); + $pen_color = rgar( $init_options, 'PenColor', '#000000' ); + + if ( $is_form_editor || $is_block_editor ) { + if ( gf_signature()->is_gravityforms_supported( '2.5-beta' ) ) { + $input = "
    "; + } else { + //box width is hardcoded in the admin + $input = '' . + "
    "; + } + } else { + + $input = ''; + + $signature_filename = ! empty( $value ) ? $value : rgpost( "{$field_id}_signature_filename" ); + $container_style = rgar( $form, 'labelPlacement', 'top_label' ) == 'top_label' ? '' : "style='display:-moz-inline-stack; display: inline-block; zoom: 1; *display: inline;'"; + + if ( ! empty( $signature_filename ) ) { + + $signature_url = $this->get_value_url( $signature_filename ); + + $input .= sprintf( "
    ", $field_id, $boxwidth, $signature_url, $boxwidth, $borderstyle, $borderwidth, $bordercolor ); + + if ( $is_entry_detail && $value ) { + + // include the download link + $download_icon = gf_signature()->is_gravityforms_supported( '2.5' ) ? '' : ''; + $input .= sprintf( + "%s", + $signature_url, + esc_attr__( 'Download file', 'gravityformssignature' ), + $download_icon + ); + + // include the delete link + $delete_icon = gf_signature()->is_gravityforms_supported( '2.5.5.4' ) ? '' : ''; + $input .= sprintf( + "%s", + esc_attr__( 'Delete file', 'gravityformssignature' ), + rgar( $entry, 'id' ), + $id, + $delete_icon + ); + + } else { + + $input .= "" . __( 'Sign again.', 'gravityformssignature' ) . ''; + } + + $input .= sprintf( "
    ", $signature_filename, $field_id, $field_id ); + + $input .= ""; + + } + + $display = ! empty( $signature_filename ) ? 'display:none;' : ''; + + $input .= "
    " . + ""; + + $use_canvas = true; + + global $is_IE; + if ( $is_IE ) { + $ua = $_SERVER['HTTP_USER_AGENT']; + + if ( ! empty( $ua ) && preg_match( '/\bMSIE (\d)/', $ua, $matches ) && (int) $matches[1] <= 8 ) { + $input .= "
    "; + $use_canvas = false; + } + } + + if ( $use_canvas ) { + $input .= ""; + } + + $input .= '
    '; + + if ( $this->is_entry_detail_edit() ) { + $input .= "'; + } + } + + return $input; + } + + /** + * Determines if the current page has the block editor. + * + * @since 4.4 + * + * @return bool Returns true if the current page is a post or page edit with block editor enabled. Returns false otherwise. + */ + public function is_block_editor() { + $is_rest_request = defined( 'REST_REQUEST' ) && REST_REQUEST; + $is_form_preview = rgget( 'context' ) == 'edit' && boolval( $_GET['attributes']['formPreview'] ); + + return $is_rest_request && $is_form_preview; + } + + /** + * Return the SuperSignature initialization scripts. + * + * @param array $form The Form Object currently being processed. + * + * @return string + */ + public function get_form_inline_script_on_page_render( $form ) { + + $unique_id = $form['id'] == 0 ? "input_{$this->id}" : 'input_' . $form['id'] . "_{$this->id}"; + $init_options = $this->get_supersignature_init_options( $unique_id, $form ); + + $script = "window.obj{$unique_id} = new SuperSignature( " . json_encode( $init_options ) . " );" . + "obj{$unique_id}.Init();" . + "jQuery('#gform_{$form['id']}').submit(function(){" . + " jQuery('#{$unique_id}_valid').val(obj{$unique_id}.IsValid() || jQuery('#{$unique_id}_signature_filename').val() ? '1' : '');" . + "});"; + + $script = "if( typeof SuperSignature != 'undefined' && jQuery('#{$unique_id}').length ) { $script }"; + + return $script; + } + + + // # SUBMISSION ----------------------------------------------------------------------------------------------------- + + /** + * Used to determine the required validation result. + * + * @param int $form_id The ID of the form currently being processed. + * + * @return bool + */ + public function is_value_submission_empty( $form_id ) { + $input_prefix = "input_{$form_id}_{$this->id}"; + $is_invalid = rgempty( "{$input_prefix}_signature_filename" ) && rgempty( "{$input_prefix}_valid" ); + + if ( $is_invalid && empty( $this->errorMessage ) ) { + $this->errorMessage = __( 'Please enter your signature.', 'gravityformssignature' ); + } + + return $is_invalid; + } + + /** + * Save the signature on submission; includes form validation or when an incomplete submission is being saved. + * + * @param array $field_values The dynamic population parameter names with their corresponding values to be populated. + * @param bool|true $get_from_post_global_var Whether to get the value from the $_POST array as opposed to $field_values. + * + * @return string + */ + public function get_value_submission( $field_values, $get_from_post_global_var = true ) { + if ( empty( $_POST[ 'is_submit_' . $this->formId ] ) ) { + return ''; + } + + return $this->maybe_save_signature(); + } + + + // # ENTRY RELATED -------------------------------------------------------------------------------------------------- + + /** + * Get the signature filename for saving to the Entry Object. + * + * @param array|string $value The value to be saved. + * @param array $form The Form Object currently being processed. + * @param string $input_name The input name used when accessing the $_POST. + * @param int $lead_id The ID of the Entry currently being processed. + * @param array $lead The Entry Object currently being processed. + * + * @return array|string + */ + public function get_value_save_entry( $value, $form, $input_name, $lead_id, $lead ) { + + return $this->maybe_save_signature(); + } + + /** + * Format the entry value for when the field/input merge tag is processed. Not called for the {all_fields} merge tag. + * + * @param string|array $value The field value. Depending on the location the merge tag is being used the following functions may have already been applied to the value: esc_html, nl2br, and urlencode. + * @param string $input_id The field or input ID from the merge tag currently being processed. + * @param array $entry The Entry Object currently being processed. + * @param array $form The Form Object currently being processed. + * @param string $modifier The merge tag modifier. e.g. value + * @param string|array $raw_value The raw field value from before any formatting was applied to $value. + * @param bool $url_encode Indicates if the urlencode function may have been applied to the $value. + * @param bool $esc_html Indicates if the esc_html function may have been applied to the $value. + * @param string $format The format requested for the location the merge is being used. Possible values: html, text or url. + * @param bool $nl2br Indicates if the nl2br function may have been applied to the $value. + * + * @return string + */ + public function get_value_merge_tag( $value, $input_id, $entry, $form, $modifier, $raw_value, $url_encode, $esc_html, $format, $nl2br ) { + if ( ! empty( $value ) ) { + $signature_url = $this->get_value_url( $value ); + + return $url_encode ? urlencode( $signature_url ) : $signature_url; + } + + return $value; + } + + /** + * Format the entry value for display on the entries list page. + * + * @param string|array $value The field value. + * @param array $entry The Entry Object currently being processed. + * @param string $field_id The field or input ID currently being processed. + * @param array $columns The properties for the columns being displayed on the entry list page. + * @param array $form The Form Object currently being processed. + * + * @return string + */ + public function get_value_entry_list( $value, $entry, $field_id, $columns, $form ) { + if ( ! empty( $value ) ) { + $signature_url = $this->get_value_url( $value ); + $thumb = gf_signature()->is_gravityforms_supported( '2.5' ) ? '' : ''; + $value = sprintf( "%s", $signature_url, esc_attr__( 'Click to view', 'gravityformssignature' ), $thumb ); + } + + return $value; + } + + /** + * Format the entry value for display on the entry detail page and for the {all_fields} merge tag. + * + * @param string|array $value The field value. + * @param string $currency The entry currency code. + * @param bool|false $use_text When processing choice based fields should the choice text be returned instead of the value. + * @param string $format The format requested for the location the merge is being used. Possible values: html, text or url. + * @param string $media The location where the value will be displayed. Possible values: screen or email. + * + * @return string + */ + public function get_value_entry_detail( $value, $currency = '', $use_text = false, $format = 'html', $media = 'screen' ) { + if ( ! empty( $value ) ) { + $signature_url = $this->get_value_url( $value ); + + if ( $format == 'html' ) { + $value = sprintf( "", $signature_url, esc_attr__( 'Click to view', 'gravityformssignature' ), $signature_url ); + } else { + $value = $signature_url; + } + + } + + return $value; + } + + /** + * Format the entry value before it is used in entry exports and by framework add-ons using GFAddOn::get_field_value(). + * + * @param array $entry The entry currently being processed. + * @param string $input_id The field or input ID. + * @param bool|false $use_text When processing choice based fields should the choice text be returned instead of the value. + * @param bool|false $is_csv Is the value going to be used in the .csv entries export? + * + * @return string + */ + public function get_value_export( $entry, $input_id = '', $use_text = false, $is_csv = false ) { + if ( empty( $input_id ) ) { + $input_id = $this->id; + } + + $value = rgar( $entry, $input_id ); + + return ! empty( $value ) ? $this->get_value_url( $value ) : ''; + } + + /** + * Returns the signature URL. + * + * @since 4.0 + * + * @param string $value The field value; the signature filename including extension. + * + * @return string + */ + public function get_value_url( $value ) { + $modifiers = $this->get_modifiers(); + $signature = new GF_Signature_Image( gf_signature(), pathinfo( $value, PATHINFO_FILENAME ), $this->formId, $this->id, in_array( 'transparent', $modifiers ), in_array( 'download', $modifiers ) ); + + return $signature->get_url(); + } + + + // # HELPERS -------------------------------------------------------------------------------------------------------- + + /** + * Save the signature if it hasn't already been saved. Delete the old signature if they used the sign again link. + * + * @return string The filename. + */ + public function maybe_save_signature() { + $form_id = $this->formId; + $id = $this->id; + + $input_name = "input_{$id}"; + $input_prefix = "input_{$form_id}_{$id}"; + $input_data = "{$input_prefix}_data"; + + $signature_data = rgpost( $input_data ); + $filename = ! rgempty( $input_name ) ? rgpost( $input_name ) : rgpost( "{$input_prefix}_signature_filename" ); + + if ( ! empty( $filename ) && ! empty( $signature_data ) ) { + gf_signature()->delete_signature_file( $filename ); + $filename = false; + } + + if ( empty( $filename ) && ! empty( $signature_data ) ) { + $filename = gf_signature()->save_signature( $input_data ); + $_POST["{$input_prefix}_signature_filename"] = $filename; + unset( $_POST[ $input_data ] ); + } + + return $filename; + } + + /** + * Retrieve the options to be used when initializing SuperSignature for this field. + * + * @param string $field_id The field canvas/div id attribute. + * @param array $form The current form object. + * + * @return array + */ + public function get_supersignature_init_options( $field_id, $form ) { + // Set init options based on device. + $user_agent = $_SERVER['HTTP_USER_AGENT']; + + if ( strpos( $user_agent, 'Mac') !== FALSE ){ + // Settings for mac. + $init_options = array( + 'Enabled' => true, + 'SignObject' => $field_id, + 'BackColor' => empty( $this->backgroundColor ) ? '#FFFFFF' : $this->backgroundColor, + 'PenSize' => rgblank( $this->penSize ) ? '2' : $this->penSize, + 'PenColor' => empty( $this->penColor ) ? '#000000' : $this->penColor, + 'SignWidth' => rgblank( $this->boxWidth ) ? '300' : $this->boxWidth, + 'SignHeight' => '180', + 'BorderStyle' => empty( $this->borderStyle ) ? 'Dashed' : $this->borderStyle, + 'BorderWidth' => rgblank( $this->borderWidth ) ? '2px' : $this->borderWidth . 'px', + 'BorderColor' => empty( $this->borderColor ) ? '#DDDDDD' : $this->borderColor, + 'RequiredPoints' => '15', + 'ClearImage' => gf_signature()->get_base_url() . '/includes/super_signature/refresh.png', + 'PenCursor' => gf_signature()->get_base_url() . '/includes/super_signature/pen.cur', + 'Visible' => true, + 'ErrorMessage' => '', + 'StartMessage' => '', + 'SuccessMessage' => '', + ); + } + else { + // Settings for pc and anything else. + $init_options = array( + 'SignObject' => $field_id, + 'BackColor' => empty( $this->backgroundColor ) ? '#FFFFFF' : $this->backgroundColor, + 'PenSize' => rgblank( $this->penSize ) ? '2' : $this->penSize, + 'PenColor' => empty( $this->penColor ) ? '#000000' : $this->penColor, + 'SignWidth' => rgblank( $this->boxWidth ) ? '300' : $this->boxWidth, + 'SignHeight' => '180', + 'BorderStyle' => empty( $this->borderStyle ) ? 'Dashed' : $this->borderStyle, + 'BorderWidth' => rgblank( $this->borderWidth ) ? '2px' : $this->borderWidth . 'px', + 'BorderColor' => empty( $this->borderColor ) ? '#DDDDDD' : $this->borderColor, + 'RequiredPoints' => '15', + 'ClearImage' => gf_signature()->get_base_url() . '/includes/super_signature/refresh.png', + 'PenCursor' => gf_signature()->get_base_url() . '/includes/super_signature/pen.cur', + 'Visible' => true, + 'ErrorMessage' => '', + 'StartMessage' => '', + 'SuccessMessage' => '', + 'forceMouseEvent' => true, + 'IeModalFix' => GFSignature::get_instance()->is_ie(), + ); + } + + /** + * Allow the SuperSignature initialization options to be customized. + * + * @param array $init_options The initialization options. + * @param GF_Field_Signature $field The current field object. + * @param array $form The current form object. + * + * @since 3.0.2 + */ + return apply_filters( 'gform_signature_init_options', $init_options, $this, $form ); + } + + + +} + +GF_Fields::register( new GF_Field_Signature() ); diff --git a/includes/class-gf-signature-image.php b/includes/class-gf-signature-image.php new file mode 100644 index 0000000..37b1beb --- /dev/null +++ b/includes/class-gf-signature-image.php @@ -0,0 +1,403 @@ +addon = $addon; + $this->filename = $filename; + $this->form_id = $form_id; + $this->field_id = $field_id; + $this->transparent = $transparent; + $this->download = $download; + $this->hash = $hash; + } + + /** + * Returns the URL for the requested signature file. + * + * @since 4.0 + * + * @return string + */ + public function get_url() { + if ( empty( $this->filename ) ) { + return ''; + } + + $url = add_query_arg( $this->get_url_args(), site_url( 'index.php' ) ); + $filter = array( 'gform_signature_url', $this->form_id, $this->field_id ); + + if ( function_exists( 'gf_has_filters' ) && gf_has_filters( $filter ) ) { + $this->addon->log_debug( __METHOD__ . '(): Executing functions hooked to gform_signature_url.' ); + } + + /** + * Allows the signature URL to be overridden. + * + * @since 4.0 + * + * @param string $url The signature URL. + * @param string $filename The signature filename excluding extension. + * @param int $form_id The ID of the form used to create the signature. + * @param int $field_id The ID of the field used to create the signature. + */ + return gf_apply_filters( $filter, $url, $this->filename, $this->form_id, $this->field_id ); + } + + /** + * Returns an array of query arguments to be added to the signature URL. + * + * @since 4.0 + * + * @return array + */ + private function get_url_args() { + $args = array( + $this->addon->get_query_var() => urlencode( $this->filename ), + 'form-id' => $this->form_id, + 'field-id' => $this->field_id, + 'hash' => GFCommon::generate_download_hash( $this->form_id, $this->field_id, $this->filename ), + ); + + if ( $this->transparent ) { + $args['t'] = 1; + } + + if ( $this->download ) { + $args['dl'] = 1; + } + + return $args; + } + + /** + * Outputs the signature image if the request includes the appropriate query data. + * + * @since 4.0 + */ + public function maybe_output() { + if ( empty( $this->filename ) ) { + return; + } + + $this->addon->log_debug( __METHOD__ . "(): Processing request for file: {$this->filename}" ); + + if ( ! empty( $this->hash ) ) { + if ( ! $this->field_exists() ) { + $this->addon->log_debug( __METHOD__ . "(): Aborting ({$this->filename}); field does not exist." ); + $this->die_404(); + } + + $this->maybe_require_login(); + + if ( ! $this->is_permission_granted() ) { + $this->addon->log_debug( __METHOD__ . "(): Aborting ({$this->filename}); permission denied." ); + $this->die_401(); + } + + $this->addon->log_debug( __METHOD__ . "(): Permission granted ({$this->filename}). Proceeding." ); + } + + $image = $this->get_image(); + + if ( ! $image ) { + $this->addon->log_debug( __METHOD__ . "(): Aborting ({$this->filename}); file does not exist." ); + $this->die_404(); + } + + $this->output( $image ); + exit(); + } + + /** + * Determines if the signature field exists. + * + * If the field no longer exists or is not a signature field then the file no longer exists. + * + * @since 4.0 + * + * @return bool + */ + public function field_exists() { + $field = GFAPI::get_field( $this->form_id, $this->field_id ); + + return $field instanceof GF_Field_Signature; + } + + /** + * Redirects to the login page if login is required to access the signature. + * + * @since 4.0 + */ + private function maybe_require_login() { + if ( ! has_filter( 'gform_signature_url_require_login' ) ) { + return; + } + + $this->addon->log_debug( __METHOD__ . '(): Executing functions hooked to gform_signature_url_require_login.' ); + + /** + * Allows login to be required to access the signature. + * + * @since 4.0 + * + * @param bool $require_login Does the user need to be logged in to access the signature? Default false. + * @param int $form_id The ID of the form used to create the signature. + * @param int $field_id The ID of the field used to create the signature. + */ + $require_login = apply_filters( 'gform_signature_url_require_login', false, $this->form_id, $this->field_id ); + + if ( $require_login && ! is_user_logged_in() ) { + $this->addon->log_debug( __METHOD__ . '(): Redirecting to the login page.' ); + auth_redirect(); + } + } + + /** + * Validates the hash to determine if the signature can be accessed. + * + * @since 4.0 + * + * @return bool + */ + private function is_permission_granted() { + $permission_granted = hash_equals( $this->hash, GFCommon::generate_download_hash( $this->form_id, $this->field_id, $this->filename ) ); + + if ( has_filter( 'gform_signature_url_permission_granted' ) ) { + $this->addon->log_debug( __METHOD__ . '(): Executing functions hooked to gform_signature_url_permission_granted.' ); + + /** + * Allow custom logic to be used to determine if the signature can be accessed. + * + * @since 4.0 + * + * @param bool $permission_granted Indicates if access to the signature has been granted. Default is the result of the hash validation. + * @param int $form_id The ID of the form used to create the signature. + * @param int $field_id The ID of the field used to create the signature. + */ + $permission_granted = apply_filters( 'gform_signature_url_permission_granted', $permission_granted, $this->form_id, $this->field_id ); + } + + return $permission_granted; + } + + /** + * Returns the resource identifier of the signature image to be output. + * + * @since 4.0 + * + * @return false|resource + */ + private function get_image() { + $this->addon->log_debug( __METHOD__ . "(): Running for file: {$this->filename}" ); + $file_path = trailingslashit( GFSignature::get_signatures_folder() ) . $this->filename . '.png'; + + if ( ! $this->is_valid_file( $file_path ) ) { + return false; + } + + // Preventing errors from being displayed. + $prev_level = error_reporting( 0 ); + + $image = imagecreatefrompng( $file_path ); + + // Restoring error reporting level. + error_reporting( $prev_level ); + + if ( ! $image || $this->is_transparent_image() ) { + return $image; + } + + return $this->get_flattened_image( $image ); + } + + /** + * Determines if the file can be used to get the signature image. + * + * @since 4.0 + * + * @param string $file_path The signature file path. + * + * @return bool + */ + private function is_valid_file( $file_path ) { + // If mime_content_type function is defined, use it to validate that the local file is a PNG, otherwise assume it is valid. + if ( stream_is_local( $file_path ) && function_exists( 'mime_content_type' ) && mime_content_type( $file_path ) !== 'image/png' ) { + return false; + } + + if ( ! is_readable( $file_path ) ) { + return false; + } + + return true; + } + + /** + * Determines if the transparent image has been requested. + * + * @since 4.0 + * + * @return bool + */ + public function is_transparent_image() { + return (bool) $this->transparent; + } + + /** + * Creates a flattened version of the signature image. + * + * @since 4.0 + * + * @param resource $image The signature resource identifier. + * + * @return resource + */ + private function get_flattened_image( $image ) { + $width = imagesx( $image ); + $height = imagesy( $image ); + $return_image = imagecreatetruecolor( $width, $height ); + + if ( ! $return_image ) { + return $image; + } + + // If any of the remaining steps in flattening the image fail destroy the new image and return the original. + if ( ! imagealphablending( $return_image, false ) || + ! imagesavealpha( $return_image, true ) || + ! imagecopyresampled( $return_image, $image, 0, 0, 0, 0, $width, $height, $width, $height ) + ) { + imagedestroy( $return_image ); + + return $image; + } + + imagedestroy( $image ); + + return $return_image; + } + + /** + * Outputs the signature image. + * + * @since 4.0 + * + * @param resource $image The signature resource identifier. + */ + private function output( $image ) { + $this->addon->log_debug( __METHOD__ . "(): Running for file: {$this->filename}" ); + + if ( ob_get_length() > 0 ) { + ob_clean(); + } + + $content_disposition = $this->download ? 'attachment' : 'inline'; + + nocache_headers(); + header( 'X-Robots-Tag: noindex', true ); + header( 'Content-Type: image/png' ); + header( 'Content-Description: File Transfer' ); + header( 'Content-Disposition: ' . $content_disposition . '; filename="' . $this->filename . '.png"' ); + header( 'Content-Transfer-Encoding: binary' ); + imagepng( $image ); + imagedestroy( $image ); + } + + /** + * Ends the request with a 404 (Not Found) HTTP status code. Loads the 404 template if it exists. + * + * @since 4.0 + */ + private function die_404() { + global $wp_query; + status_header( 404 ); + $wp_query->set_404(); + $template_path = get_404_template(); + if ( file_exists( $template_path ) ) { + require_once( $template_path ); + } + die(); + } + + /** + * Ends the request with a 401 (Unauthorized) HTTP status code. + * + * @since 4.0 + */ + private function die_401() { + status_header( 401 ); + die(); + } + +} diff --git a/includes/super_signature/README.txt b/includes/super_signature/README.txt new file mode 100644 index 0000000..3e20136 --- /dev/null +++ b/includes/super_signature/README.txt @@ -0,0 +1,10 @@ +READ ME - Important +*************************************************** + +Updated: February 13, 2012 7:05 PM + +Cursor (pen.cur) image created by Vlastimil Miléř and made available via the Creative Commons Attribution License. + +http://www.rw-designer.com/user/vlasta +http://www.rw-designer.com/cursor-detail/81 +http://www.rw-designer.com/licenses \ No newline at end of file diff --git a/includes/super_signature/base64.js b/includes/super_signature/base64.js new file mode 100755 index 0000000..8fe652b --- /dev/null +++ b/includes/super_signature/base64.js @@ -0,0 +1,135 @@ +var Base64 = { + + // private property + _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", + + // public method for encoding + encode : function (input) { + var output = ""; + var chr1, chr2, chr3, enc1, enc2, enc3, enc4; + var i = 0; + + input = Base64._utf8_encode(input); + + while (i < input.length) { + + chr1 = input.charCodeAt(i++); + chr2 = input.charCodeAt(i++); + chr3 = input.charCodeAt(i++); + + enc1 = chr1 >> 2; + enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); + enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); + enc4 = chr3 & 63; + + if (isNaN(chr2)) { + enc3 = enc4 = 64; + } else if (isNaN(chr3)) { + enc4 = 64; + } + + output = output + + this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + + this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); + + } + + return output; + }, + + // public method for decoding + decode : function (input) { + var output = ""; + var chr1, chr2, chr3; + var enc1, enc2, enc3, enc4; + var i = 0; + + input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); + + while (i < input.length) { + + enc1 = this._keyStr.indexOf(input.charAt(i++)); + enc2 = this._keyStr.indexOf(input.charAt(i++)); + enc3 = this._keyStr.indexOf(input.charAt(i++)); + enc4 = this._keyStr.indexOf(input.charAt(i++)); + + chr1 = (enc1 << 2) | (enc2 >> 4); + chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); + chr3 = ((enc3 & 3) << 6) | enc4; + + output = output + String.fromCharCode(chr1); + + if (enc3 != 64) { + output = output + String.fromCharCode(chr2); + } + if (enc4 != 64) { + output = output + String.fromCharCode(chr3); + } + + } + + output = Base64._utf8_decode(output); + + return output; + + }, + + // private method for UTF-8 encoding + _utf8_encode : function (string) { + string = string.replace(/\r\n/g,"\n"); + var utftext = ""; + + for (var n = 0; n < string.length; n++) { + + var c = string.charCodeAt(n); + + if (c < 128) { + utftext += String.fromCharCode(c); + } + else if((c > 127) && (c < 2048)) { + utftext += String.fromCharCode((c >> 6) | 192); + utftext += String.fromCharCode((c & 63) | 128); + } + else { + utftext += String.fromCharCode((c >> 12) | 224); + utftext += String.fromCharCode(((c >> 6) & 63) | 128); + utftext += String.fromCharCode((c & 63) | 128); + } + + } + + return utftext; + }, + + // private method for UTF-8 decoding + _utf8_decode : function (utftext) { + var string = ""; + var i = 0; + var c = c1 = c2 = 0; + + while ( i < utftext.length ) { + + c = utftext.charCodeAt(i); + + if (c < 128) { + string += String.fromCharCode(c); + i++; + } + else if((c > 191) && (c < 224)) { + c2 = utftext.charCodeAt(i+1); + string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); + i += 2; + } + else { + c2 = utftext.charCodeAt(i+1); + c3 = utftext.charCodeAt(i+2); + string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); + i += 3; + } + + } + + return string; + } + +} \ No newline at end of file diff --git a/includes/super_signature/license.php b/includes/super_signature/license.php new file mode 100755 index 0000000..8c808be --- /dev/null +++ b/includes/super_signature/license.php @@ -0,0 +1,164 @@ + 0) + { + $signDataSmooth = str_replace('data:image/png;base64,', '', $signDataSmooth); + $signDataSmooth = str_replace(' ', '+', $signDataSmooth); + $data = base64_decode($signDataSmooth); + + $im = imagecreatefromstring($data); + + return $im; + } + + return null; +} + +function GetSignatureImage($signData) +{ + $base64DecData = base64_decode($signData); + + + $exploded_sign = explode(';', $base64DecData); + + $explode_firstRow = explode(',', $exploded_sign[0]); + + + if(count($explode_firstRow) == 8) + { + // process ahead if there are enough data in first input + + $SignBackGround = Html2RGB($explode_firstRow[1]); + $SignWidth = $explode_firstRow[3]; + $SignHeight = $explode_firstRow[4]; + $SignTransparent = strtoupper($explode_firstRow[5]); + $SignPoints = (integer)$explode_firstRow[6]; + $SignControl = $explode_firstRow[7]; + + $im = imagecreatetruecolor((int)$SignWidth,(int)$SignHeight); + + $colBack = imagecolorallocate($im, (int)$SignBackGround[0], (int)$SignBackGround[1], (int)$SignBackGround[2]); + + imagefill($im, 0, 0, $colBack); + + if($SignTransparent == "TRUE") + { + imagecolortransparent($im, $colBack); + } + + // Now get rest of points + + + for ( $counter = 1; $counter < count($exploded_sign); $counter ++) + { + if(strlen($exploded_sign[$counter]) > 0) + { + // Keep processing points + $exploded_PointData = explode(" ", trim($exploded_sign[$counter])); + + $exploded_FirstPointData = explode(',', $exploded_PointData[0]); + + $SignThick = $exploded_FirstPointData[0]; + $SignPenColor = Html2RGB($exploded_FirstPointData[1]); + + $penColor = imagecolorallocate($im, (int)$SignPenColor[0], (int)$SignPenColor[1], (int)$SignPenColor[2]); + + + // Now run loop for rest of the points + + if(count($exploded_PointData) == 2) + { + $coXY = explode(',', trim($exploded_PointData[1])); + ImageFilledArc($im, (int)$coXY[0], (int)$coXY[1], (int)(2 * $SignThick), (int)(2 * $SignThick), 0, 360, $penColor, IMG_ARC_PIE); + } + else + { + for ( $Incounter = 1; $Incounter < count($exploded_PointData) - 1; $Incounter ++) + { + $coXY = explode(',', trim($exploded_PointData[$Incounter])); + $coXY2 = explode(',', trim($exploded_PointData[$Incounter + 1])); + + imgdrawLine($im, $coXY[0], $coXY[1], $coXY2[0], $coXY2[1], $penColor, $SignThick); + imgdrawLine($im, $coXY[0], $coXY[1], $coXY2[0], $coXY2[1], $penColor, $SignThick + 1); + + } + } + } + } + + return $im; + } + + return null; +} + +function imgdrawLine($image,$x0, $y0,$x1, $y1,$color,$radius) +{ + if($x0==null || $y0 == null) + return; + + if($x1==null || $y1 == null) + return; + + $radius = abs($radius / 2); + $f = 1 - $radius; + $ddF_x= 1; + $ddF_y = -2 * $radius; + $x= 0; + $y = $radius; + imageline($image,(int)$x0, (int)($y0 + $radius),(int)$x1, (int)($y1 + $radius),(int)$color); + imageline($image,(int)$x0, (int)($y0 - $radius),(int)$x1, (int)($y1 - $radius),(int)$color); + imageline($image,(int)($x0 + $radius), (int)$y0,(int)($x1 + $radius), (int)$y1,(int)$color); + imageline($image,(int)($x0 - $radius), (int)$y0,(int)($x1 - $radius), (int)$y1,(int)$color); + + while($x< $y) + { + if($f >= 0) + { + $y--; + $ddF_y += 2; + $f += $ddF_y; + } + $x++; + $ddF_x+= 2; + $f += $ddF_x; + imageline($image,$x0 + $x, (int)($y0 + $y),$x1 + $x, (int)($y1+ $y),(int)$color); + imageline($image,$x0 - $x, (int)($y0 + $y),$x1 - $x, (int)($y1 + $y),(int)$color); + imageline($image,$x0 + $x, (int)($y0 - $y),$x1 + $x, (int)($y1 - $y),(int)$color); + imageline($image,$x0 - $x, (int)($y0 - $y),$x1 - $x, (int)($y1 - $y),(int)$color); + imageline($image,(int)($x0 + $y), $y0 + $x,(int)($x1 + $y), $y1 + $x,(int)$color); + imageline($image,(int)($x0 - $y), $y0 + $x,(int)($x1 - $y), $y1 + $x,(int)$color); + imageline($image,(int)($x0 + $y), $y0 - $x,(int)($x1 + $y), $y1 - $x,(int)$color); + imageline($image,(int)($x0 - $y), $y0 - $x,(int)($x1 - $y), $y1 - $x,(int)$color); + + } +} + +function Html2RGB($color) +{ + if ($color[0] == '#') + $color = substr($color, 1); + + if (strlen($color) == 6) + list($r, $g, $b) = array($color[0].$color[1], + $color[2].$color[3], + $color[4].$color[5]); + elseif (strlen($color) == 3) + list($r, $g, $b) = array($color[0].$color[0], $color[1].$color[1], $color[2].$color[2]); + else + return false; + + $r = hexdec($r); $g = hexdec($g); $b = hexdec($b); + + return array($r, $g, $b); +} diff --git a/includes/super_signature/pen.cur b/includes/super_signature/pen.cur new file mode 100755 index 0000000..9f5c0c1 Binary files /dev/null and b/includes/super_signature/pen.cur differ diff --git a/includes/super_signature/pencil.cur b/includes/super_signature/pencil.cur new file mode 100755 index 0000000..58e3fb2 Binary files /dev/null and b/includes/super_signature/pencil.cur differ diff --git a/includes/super_signature/refresh.png b/includes/super_signature/refresh.png new file mode 100644 index 0000000..71e0ec3 Binary files /dev/null and b/includes/super_signature/refresh.png differ diff --git a/includes/super_signature/ss.js b/includes/super_signature/ss.js new file mode 100644 index 0000000..41e9886 --- /dev/null +++ b/includes/super_signature/ss.js @@ -0,0 +1,67 @@ +/* +* Copyright 2020-2021, www.SuperSignature.com +* This code is not a freeware. You need to buy it before use! +* ver 1.5.0.4 September 2019 +*/ + +var $ = jQuery.noConflict(); + +ValidateSignature=function(b){var g=!0;if(null==b||"undefined"==b||""==b)for(b=0;b"+V.innerHTML}catch(c){alert(c.description)}}function p(a){a=a.replace("undefined ","");a=a.replace("undefined","");var c="",d,f=0;a=a.replace(/\x0d\x0a/g,"\n");var l="";for(d=0;du?l+=String.fromCharCode(u):(127u?l+=String.fromCharCode(u>>6|192):(l+=String.fromCharCode(u>>12|224),l+=String.fromCharCode(u>>6&63|128)),l+=String.fromCharCode(u&63|128))}for(a=l;f>2;A=(A&3)<<4|l>>4;var Y=(l&15)<<2|d>>6;var v=d&63;isNaN(l)?Y=v=64:isNaN(d)&&(v=64);c=c+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(u)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(A)+ + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(Y)+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".charAt(v)}return c}function r(){w[0]=0;for(var a=1;a=ra?!0:!1;sa=w[0];a="";J[0]="1,"+W+","+ta+","+F+","+G+","+oa+","+w[0]+","+C+";";for(var c=0;c=J.length)this.ResetClick();else{J.pop();P.pop();w.pop();ba.pop();r();var a="'"+H.value+"'";var c= + "",d=0;/[^A-Za-z0-9\+\/=]/g.exec(a);a=a.replace(/[^A-Za-z0-9\+\/=]/g,"");do{var f="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(a.charAt(d++));var l="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(a.charAt(d++));var u="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(a.charAt(d++));var A="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".indexOf(a.charAt(d++));f=f<<2|l>>4;l=(l&15)<<4|u>>2;var Y=(u& + 3)<<6|A;c+=String.fromCharCode(f);64!=u&&(c+=String.fromCharCode(l));64!=A&&(c+=String.fromCharCode(Y))}while(d');e.pathCoordString=za;e.insertAdjacentHTML("beforeEnd",Ea)}else e.beginPath(),e.lineJoin="round",e.lineCap="round",e.moveTo(x,v);2==u.length&&eval("obj"+C).DrawDot(x,v)}else if(x= + Math.abs(parseInt(x,0)*c),v=Math.abs(parseInt(v,0)*c),isIE||ia){if(e.pathCoordString+=" "+x+","+v,x=document.getElementById(C+"_l_"+(f-1)))if(x=x.firstChild)try{x.setAttribute("v",e.pathCoordString+" e")}catch(Fa){}}else e.strokeStyle=U,e.lineWidth=L,e.lineTo(x,v),e.stroke(),e.moveTo(x,v);isIE?e.innerHTML=e.innerHTML:(e.closePath(),e.restore())}B++}!isIE&&ia&&q(c);r()};this.TextSignature=function(a,c,d,f){da&&(e.font=a,e.fillText(c,d,f))};this.CheckCompatibility=function(){if(isIE)this.IsCompatible= + !0,isMobileIE||document.namespaces.SuperSignature||document.namespaces.add("SuperSignature","urn:schemas-microsoft-com:vml","#default#VML");else{try{da=!!document.createElement("canvas").getContext("2d")}catch(a){da=!!document.createElement("canvas").getContext}da?this.IsCompatible=!0:document.write("Your browser does not support our signature control.")}};this.MouseMove=function(a){if(I&&pa){"undefined"!==typeof a.originalEvent&&(a=a.originalEvent);M(a);var c=$("#"+m.id).offset();if(D&&"undefined"!== + typeof a.targetTouches){var d=a.targetTouches[0];var f=d.clientX-ma;d=d.clientY-la;ja&&(d-=parseInt($(document).scrollTop()),f-=parseInt($(document).scrollLeft()))}else a.originalEvent?a.pageX?(f=parseInt(a.pageX-c.left),d=parseInt(a.pageY-c.top)):(f=parseInt(a.originalEvent.pageX-c.left),d=parseInt(a.originalEvent.pageY-c.top)):isIE||isIENine?(f=parseInt(a.x),d=parseInt(a.y),9<=iever&&(f=parseInt(a.pageX-c.left),d=parseInt(a.pageY-c.top))):(f=parseInt(a.pageX-c.left),d=parseInt(a.pageY-c.top));fa&& + ha&&(f=parseInt(a.offsetX||a.layerX),d=parseInt(a.offsetY||a.layerY));(ca||ea)&&-1==a.type.indexOf("touch")&&(f=a.clientX-O.getBoundingClientRect().left,d=a.clientY-O.getBoundingClientRect().top);isMobileIE?K.push(Math.abs(parseInt(f)-parseInt(m.offsetLeft))+","+Math.abs(parseInt(d)-parseInt(m.offsetTop))):K.push(Math.abs(parseInt(f))+","+Math.abs(parseInt(d)));w[B]++;if(isIE)if(isMobileIE){if(a=f-Z,c=d-aa,64')):(e.arc(a,c,L/2,0,2*Math.PI,!1),e.fill(),e.stroke())};this.MouseOut=function(a){I&&(M(a),h("Mouse out"),ka||X())};this.MouseUp=function(a){if(I){"undefined"!==typeof a.originalEvent&&(a=a.originalEvent);M(a);h("Mouse up");if(null!=na){var c=parseInt(new Date-na);if(125>c){h("Time diff "+c);if(D)c=wa,a=xa;else{var d=$("#"+m.id).offset();a.originalEvent?a.pageX?(c=parseInt(a.pageX-d.left),a=parseInt(a.pageY-d.top)): + (c=parseInt(a.originalEvent.pageX-d.left),a=parseInt(a.originalEvent.pageY-d.top)):isIE||isIENine?(c=parseInt(a.x),a=parseInt(a.y)):(c=parseInt(a.pageX-d.left),a=parseInt(a.pageY-d.top))}isIE&&(h("Drawing Dot At ("+c+","+a+")"),eval("obj"+C).DrawDot(c,a))}na=null}ka||X()}};this.MouseDown=function(a){if(I){"undefined"!==typeof a.originalEvent&&(a=a.originalEvent);M(a);na=new Date;pa=!0;ka=!1;if(D&&"undefined"!==typeof a.targetTouches){var c=a.targetTouches[0];ma=O.getBoundingClientRect().left;la=O.getBoundingClientRect().top; + var d=c.clientX-ma;c=c.clientY-la;ja&&(c-=parseInt($(document).scrollTop()),d-=parseInt($(document).scrollLeft()));wa=d;xa=c}else{var f=$("#"+m.id).offset();a.originalEvent?a.pageX?(d=parseInt(a.pageX-f.left),c=parseInt(a.pageY-f.top)):(d=parseInt(a.originalEvent.pageX-f.left),c=parseInt(a.originalEvent.pageY-f.top)):isIE||isIENine?9>iever?(d=parseInt(a.x),c=parseInt(a.y)):isIENine&&(d=parseInt(a.pageX)-parseInt(f.left)+parseInt($("html").css("margin-left")),c=parseInt(a.pageY)-parseInt(f.top)+parseInt($("html").css("margin-top")), + 10<=iever&&(d=parseInt(a.pageX-f.left),c=parseInt(a.pageY-f.top))):(d=parseInt(a.pageX-f.left),c=parseInt(a.pageY-f.top))}fa&&ha&&(d=parseInt(a.offsetX||a.layerX),c=parseInt(a.offsetY||a.layerY));(ca||ea)&&-1==a.type.indexOf("touch")&&(d=a.clientX-O.getBoundingClientRect().left,c=a.clientY-O.getBoundingClientRect().top);h("Down ("+d+","+c+")");B++;w[B]=0;K.length=0;K[0]=L+","+U;isMobileIE?K.push(Math.abs(parseInt(d)-parseInt(m.offsetLeft))+","+Math.abs(parseInt(c)-parseInt(m.offsetTop))):K.push(Math.abs(parseInt(d))+ + ","+Math.abs(parseInt(c)));isIE?isMobileIE?(Z=d,aa=c):(a='"m'+d+","+c+" l"+d+","+c,d=''),e.pathCoordString=a,e.insertAdjacentHTML("beforeEnd",d)):(e.save(),e.beginPath(),e.lineJoin="round",e.lineCap="round",e.strokeStyle= + U,e.lineWidth=L,e.moveTo(d,c));return!1}};this.ResetClick=function(a){I&&(isMobileIE||(document.getElementById(C).style.borderColor=Ca),isIE?(e.innerHTML="",isMobileIE&&(aa=Z=0,null!=E&&"undefined"!=E&&(E.clear(),E.paint())),0";$("#"+this.SignObject).replaceWith(a);h("DIV changed to CANVAS");isIE=!1}}else"CANVAS"==document.getElementById(this.SignObject).nodeName.toUpperCase()&&(a="
    ",$("#"+this.SignObject).replaceWith(a),h("CANVAS changed to DIV"),isIE=!0);V=document.getElementById(this.SignObject+"_Debug");e=document.getElementById(this.SignObject);m=document.getElementById(this.SignObject+"_Container");if(e.addEventListener)h("addEventListener supported");else if(e.attachEvent)h("attachEvent supported");else{h("Mouse events are not supported");return}this.mouseMoved=!1;if(null!=e&&"undefined"!=e){h("Objects OK");isIE&&!isMobileIE&& + (ua=document.documentMode?8<=document.documentMode:!1);isMobileIE&&h("Mobile IE");isIENine&&h("IE 9 Or Above");isOperaMini&&h("Opera Mini, not supported.");w[0]=0;J[0]="1,"+W+","+ta+","+F+","+G+","+oa+","+w[0]+","+C+";";if(this.ApplyStyle){h("Setting up style");m.style.zIndex=this.SignzIndex;try{isMobileIE?(m.style.borderWidth=this.BorderWidth,m.style.borderStyle=this.BorderStyle,m.style.borderColor=this.BorderColor,m.style.backgroundColor=this.BackColor,0Clear Signature'; + a+='
    '+this.StartMessage+"
    ";a+=null==document.getElementById(this.SignObject+"_data")?'':"";a+=null==document.getElementById(this.SignObject+"_data_smooth")?'': + "";a+=null==document.getElementById(this.SignObject+"_data_canvas")?'':"";a+="";h("Setting up tools");$("#"+m.id).after(a);B=0;a="mousedown";var c="mouseup",d="mousemove",f="mouseout";isIE&&(f="mouseleave");isAndroid=-1b.length)return b[0];this.points=[];for(var k=0;k= + h;h++){if(p=this.gB(b,g,q,k,n),r=this.fM(b,g,p,q),r.error=y)break;this.rP(b,g,q,p);y=r.error}q=this.points[t-1].MathFunc2(this.points[t]);y=this.points[t].MathFunc2(this.points[t+1]);q=q.MathFunc1(y).MathFunc4(2).nOR();this.processFitPoints(b,t,k,q);this.processFitPoints(t,g,q.MathFunc5(),n)}},savePoints:function(b){this.result.push({x:this.r2(b[1].x),y:this.r2(b[1].y+this.offSet)});this.result.push({x:this.r2(b[2].x),y:this.r2(b[2].y+this.offSet)}); + this.result.push({x:this.r2(b[3].x),y:this.r2(b[3].y+this.offSet)})},r2:function(b){return Math.round(100*b)/100},gB:function(b,g,k,n,h){var p=1E-11,r=this.points[b],t=this.points[g],q=[[0,0],[0,0]],y=[0,0],X,M,N,B,D,e,O,m,Q=0;for(g=g-b+1;Qp?(B=q[0][0]*y[1]-q[1][0]*y[0],D=y[0]*q[1][1]-y[1]*q[0][1],M=D/X,N=B/X):(e=q[0][0]+q[0][1],O=q[1][0]+q[1][1],M=Math.abs(e)>p?N=y[0]/e:Math.abs(e)>p?N=y[1]/O:N=0),m=t.MathFunc6(r),p*=m,(M=p;p++)n[p]=b[p+1].MathFunc2(b[p]).MathFunc3(3);for(p=0;1>=p;p++)h[p]=n[p+ + 1].MathFunc2(n[p]).MathFunc3(2);b=this.eV(3,b,k);n=this.eV(2,n,k);h=this.eV(1,h,k);g=b.MathFunc2(g);h=n.dot(n)+g.dot(h);return 1E-5>Math.abs(h)?k:k-g.dot(n)/h},eV:function(b,g,k){g=g.slice();for(var n,h=1;h<=b;h++)for(n=0;n<=b-h;n++)g[n]=g[n].MathFunc3(1-k).MathFunc1(g[n+1].MathFunc3(k));return g[0]},cLP:function(b,g){for(var k=[0],n,h=b+1;h<=g;h++)k[h-b]=k[h-b-1]+this.points[h].MathFunc6(this.points[h-1]);h=1;for(n=g-b;h<=n;h++)k[h]/=k[n];return k},fM:function(b,g,k,n){for(var h=Math.floor((g-b+ + 1)/2),p=0,r=b+1;r=p&&(p=t,h=r)}return{error:p,index:h}}};Smooth=function(b,g){this.x=b;this.y=g}; +Smooth.prototype={CheckPtArray:function(b){return"number"==typeof b?{x:b,y:b}:b},MathFunc1:function(b){b=this.CheckPtArray(b);return SmoothRet(this.x+b.x,this.y+b.y)},MathFunc2:function(b){b=this.CheckPtArray(b);return SmoothRet(this.x-b.x,this.y-b.y)},MathFunc3:function(b){b=this.CheckPtArray(b);return SmoothRet(this.x*b.x,this.y*b.y)},MathFunc4:function(b){b=this.CheckPtArray(b);return SmoothRet(this.x/b.x,this.y/b.y)},MathFunc5:function(){return SmoothRet(-this.x,-this.y)},MathFunc6:function(b, + g){var k=b.x-this.x,n=b.y-this.y;k=k*k+n*n;return g?k:Math.sqrt(k)},getLength:function(b){var g=this.x*this.x+this.y*this.y;return b?g:Math.sqrt(g)},nOR:function(b){void 0===b&&(b=1);var g=this.getLength();b=0!=g?b/g:0;return SmoothRet(this.x*b,this.y*b)},CheckValid:function(b){return null==b?!1:this.x==b.x&&this.y==b.y},dot:function(b){return this.x*b.x+this.y*b.y}};var SmoothRet=function(b,g){return new Smooth(b,g)}; +Compress=function(b,g){var k=0,n=0,h=b.length,p="",r;g=Math.pow(10,g);for(r=0;rb&&(b=~b);for(g="";32<=b;)g+=String.fromCharCode((32|b&31)+63),b>>=5;return g+String.fromCharCode(b+63)}; +String.prototype.replaceAll=function(b,g,k){return this.replace(new RegExp(b.replace(/([\/,!\\\^\$\{\}\[\]\(\)\.\*\+\?\|<>\-&])/g,"\\$&"),k?"gi":"g"),"string"==typeof g?g.replace(/\$/g,"$$$$"):g)}; + +/* End */ \ No newline at end of file diff --git a/js/frontend.js b/js/frontend.js new file mode 100644 index 0000000..b70af18 --- /dev/null +++ b/js/frontend.js @@ -0,0 +1,230 @@ +/* global jQuery, Base64, SignatureEnabled, ResizeSignature, ClearSignature, LoadSignature */ +/* eslint-disable new-cap */ + +/** + * Handles updating of the signature field on document resize. + * + * @return {void} + */ +function gformSignatureResize( $instance ) { + /** + * Get the cached components of the signature field. + * + * @since 4.1 + * + * @param {Object} $signatureContainer jQuery signature container object. + * @return {Object} Signature data object. + */ + function getSignature( $signatureContainer ) { + var $gfield = $signatureContainer.closest( '.gfield' ); + return { + $container: $signatureContainer, + fieldID: $gfield.find( '.gfield_label' ).attr( 'for' ), + gfieldWidth: $gfield.innerWidth(), + width: $signatureContainer.data( 'original-width' ), + dataInput: $signatureContainer.parent().find( 'input[name$="_data"]:eq( 0 )' ), + fieldExists: function() { + return typeof this.fieldID !== 'undefined'; + }, + dataInputExists: function() { + return this.dataInput.length > 0; + } + }; + } + + /** + * Get the resized width of the signature. + * + * @since 4.1 + * + * @param {Object} signature The signature data object. + * @return {*|null} The new signature width. + */ + function getNewSignatureWidth( signature ) { + return signature.gfieldWidth > signature.width ? signature.width : signature.gfieldWidth; + } + + /** + * Get the resized height of the signature. + * + * @since 4.1 + * + * @param {Number} resizedSignatureWidth The width of the resized signature. + * @param {object} signature The original signature data. + * @return {number} The new signature height. + */ + function getNewSignatureHeight( resizedSignatureWidth, signature ) { + return Math.round( resizedSignatureWidth * signature.$container.data( 'ratio' ) ); + } + + /** + * Get an object representing the signature's new height and width. + * + * @since 4.1 + * + * @param {Object} signature The original signature object. + * @return {{width: (*|null), height: number}} The resized signature data. + */ + function getResizedSignatureDimensions( signature ) { + return { + width: getNewSignatureWidth( signature ), + height: getNewSignatureHeight( getNewSignatureWidth( signature ), signature ) + }; + } + + /** + * Initializes the signature resize functionality. + * + * @since 4.3 + * + * @param {Object} $instance jQuery signature container object. + */ + function init( $instance ) { + var $this = $instance; + + // Reset the width on the signature field containers before we update our + // dimensions. This helps us get around a difference in how CSS grid sizes a + // parent container's width when a child has an explicitly set width greater + // than the parent in Firefox. + if ( + ( typeof $this.closest( '.gfield' ).find( '.gfield_label' ).attr( 'for' ) !== 'undefined' ) && + $this.parent().find( 'input[name$="_data"]:eq( 0 )' ).length > 0 + ) { + $this.css( 'width', '' ).find( 'canvas' ).css( 'width', '' ).attr( 'width', '' ); + $this.next().css( 'width', '' ); + } + + // Update field dimensions + var signature = getSignature( $this ); + var resizedSignature = getResizedSignatureDimensions( signature ); + var fieldWidth = resizedSignature.width; + var fieldHeight = resizedSignature.height > 180 ? resizedSignature.height : 180; + + if ( ! signature.fieldExists() || ! signature.dataInputExists() ) { + return; + } + + var decodedSignatureData = Base64.decode( signature.dataInput.val() ); + + // If we have a signature, let's lock the field down and show the locked reset button. + if ( decodedSignatureData ) { + SignatureEnabled( signature.fieldID, false ); + jQuery( '#' + signature.fieldID + '_lockedReset' ).show(); + // If the width field setting is greater than the field's available width, + // let's not resize the field and instead use the signed signature's dimensions. + if ( signature.width > resizedSignature.width ) { + var signatureDimensions = decodedSignatureData.split(';')[0]; + fieldWidth = signatureDimensions.split(',')[3]; + fieldHeight = signatureDimensions.split(',')[4]; + } + } + + // Resize signature. + ResizeSignature( signature.fieldID, fieldWidth, fieldHeight ); + // Set toolbar width to new signature area width + $this.closest( '.gfield_signature_ui_container' ).find('div:last-child').css('width', resizedSignature.width); + ClearSignature( signature.fieldID ); + + if ( decodedSignatureData ) { + LoadSignature( signature.fieldID, decodedSignatureData, 1 ); + } + } + + init( $instance ); +} + +/** + * Handles initialization of all reset buttons and resizing logic + * + * @since 4.4 + * + * @param form_id Current form ID. + */ +function gformSignatureInit( parent ) { + parent.find( '.gfield_signature_container' ).each( function() { + + var $this = jQuery( this ); + + // If original width is already set, exit. + if ( $this.data( 'original-width' ) ) { + return; + } + + var width = parseFloat( $this.css( 'width' ) ); + var height = parseFloat( $this.css( 'height' ) ); + var containerID = $this.attr( 'id' ).replace( '_Container', '' ); + var $resetButton = jQuery( '#' + containerID + '_resetbutton' ); + + // Add a locked reset button for when a signature is present and resized. + $resetButton.parent().append( '' ); + var $lockedResetButton = jQuery( '#' + containerID + '_lockedReset' ); + + // Force reset button to work even when Signature is disabled. + $resetButton.click( function() { + SignatureEnabled( containerID, true ); + ClearSignature( containerID ); + gformSignatureResize( $this ); + // Hide the locked reset resize button + $lockedResetButton.hide(); + } ); + + $lockedResetButton.click( function() { + jQuery( this ).hide(); + $resetButton.click(); + } ); + + // Setup the "Sign Again" icons + jQuery( '#' + containerID + '_signAgain, #' + containerID + '_lockedSignAgain' ).each( function() { + jQuery( this ).click( function( e ) { + e.preventDefault(); + jQuery( '#' + containerID + '_signature_image' ).hide(); + jQuery( '#' + containerID + '_Container' ).show(); + } ); + }); + + // Hide the status box so that our Locked Reset button display left-aligned. + jQuery( '#' + containerID + '_status' ).hide(); + + $this.data( 'ratio', height / width ); + $this.data( 'original-width', width ); + + setTimeout(function () { gformSignatureResize( $this ); }, 0 ); + } ); +} + +jQuery( window ).on( 'gform_post_render', function( event, form_id ) { + const parent = jQuery( '#gform_' + form_id ); + gformSignatureInit( parent ); +} ); + +jQuery( document ).ready( function( $ ) { + + var windowWidth = window.innerWidth + + $( window ).on( 'resize', function() { + + // Check width has actually changed as mobile devices have a variety of situations + // in which they can trigger the resize event such as when scrolling. When this happens + // it creates issues with the signature field functionality based on our customizations + // to have this field be responsive. + if ( window.innerWidth === windowWidth ) { + return; + } + + windowWidth = window.innerWidth; + + jQuery( '.gfield_signature_container' ).each( function() { + var $this = jQuery( this ); + setTimeout(function () { gformSignatureResize( $this ); }, 200 ); + } ); + + } ); + + gform.addAction( 'gform_post_conditional_logic_field_action', function ( formId, action, targetId, defaultValues, isInit ) { + var $this = $( targetId ).find( '.gfield_signature_container' ); + if ( ! isInit && action === 'show' && $this.length ) { + gformSignatureResize( $this ); + } + } ); + +} ); diff --git a/js/frontend.min.js b/js/frontend.min.js new file mode 100644 index 0000000..eba08c8 --- /dev/null +++ b/js/frontend.min.js @@ -0,0 +1 @@ +function gformSignatureResize(i){function e(i){return i.gfieldWidth>i.width?i.width:i.gfieldWidth}function t(i){return{width:e(i),height:(t=e(i),Math.round(t*i.$container.data("ratio")))};var t}var n,r,a,d,o,u;void 0!==(i=i).closest(".gfield").find(".gfield_label").attr("for")&&0d.width)&&(o=(r=n.split(";")[0]).split(",")[3],u=r.split(",")[4]),ResizeSignature(a.fieldID,o,u),i.closest(".gfield_signature_ui_container").find("div:last-child").css("width",d.width),ClearSignature(a.fieldID),n)&&LoadSignature(a.fieldID,n,1)}function gformSignatureInit(i){i.find(".gfield_signature_container").each(function(){var i,t,e,n,r,a=jQuery(this);a.data("original-width")||(i=parseFloat(a.css("width")),t=parseFloat(a.css("height")),e=a.attr("id").replace("_Container",""),(n=jQuery("#"+e+"_resetbutton")).parent().append('"),r=jQuery("#"+e+"_lockedReset"),n.click(function(){SignatureEnabled(e,!0),ClearSignature(e),gformSignatureResize(a),r.hide()}),r.click(function(){jQuery(this).hide(),n.click()}),jQuery("#"+e+"_signAgain, #"+e+"_lockedSignAgain").each(function(){jQuery(this).click(function(i){i.preventDefault(),jQuery("#"+e+"_signature_image").hide(),jQuery("#"+e+"_Container").show()})}),jQuery("#"+e+"_status").hide(),a.data("ratio",t/i),a.data("original-width",i),setTimeout(function(){gformSignatureResize(a)},0))})}jQuery(window).on("gform_post_render",function(i,t){gformSignatureInit(jQuery("#gform_"+t))}),jQuery(document).ready(function(a){var i=window.innerWidth;a(window).on("resize",function(){window.innerWidth!==i&&(i=window.innerWidth,jQuery(".gfield_signature_container").each(function(){var i=jQuery(this);setTimeout(function(){gformSignatureResize(i)},200)}))}),gform.addAction("gform_post_conditional_logic_field_action",function(i,t,e,n,r){e=a(e).find(".gfield_signature_container");!r&&"show"===t&&e.length&&gformSignatureResize(e)})}); \ No newline at end of file diff --git a/js/gfsignature_delete_signature.js b/js/gfsignature_delete_signature.js new file mode 100644 index 0000000..d3d6665 --- /dev/null +++ b/js/gfsignature_delete_signature.js @@ -0,0 +1,30 @@ +/* global gform_signature_delete_signature_strings */ + +/** + * Handle delete signature. + * + * @param leadId + * @param fieldId + */ +function deleteSignature( leadId, fieldId ) { + if ( ! confirm( gform_signature_delete_signature_strings.confirm_delete ) ) { + return; + } + + jQuery.post( + ajaxurl, + { + lead_id: leadId, + field_id: fieldId, + action: 'gf_delete_signature', + gf_delete_signature: gform_signature_delete_signature_strings.delete_nonce + }, + function ( response ) { + var formId = gform_signature_delete_signature_strings.form_id; + jQuery( '#input_' + formId + '_' + fieldId + '_signature_filename' ).val( '' ); + jQuery( '#input_' + formId + '_' + fieldId + '_signature_image' ).hide(); + jQuery( '#input_' + formId + '_' + fieldId + '_Container' ).show(); + jQuery( '#input_' + formId + '_' + fieldId + '_resetbutton' ).show(); + } + ); +} diff --git a/js/gfsignature_delete_signature.min.js b/js/gfsignature_delete_signature.min.js new file mode 100644 index 0000000..2ef9912 --- /dev/null +++ b/js/gfsignature_delete_signature.min.js @@ -0,0 +1 @@ +function deleteSignature(e,t){confirm(gform_signature_delete_signature_strings.confirm_delete)&&jQuery.post(ajaxurl,{lead_id:e,field_id:t,action:"gf_delete_signature",gf_delete_signature:gform_signature_delete_signature_strings.delete_nonce},function(e){var _=gform_signature_delete_signature_strings.form_id;jQuery("#input_"+_+"_"+t+"_signature_filename").val(""),jQuery("#input_"+_+"_"+t+"_signature_image").hide(),jQuery("#input_"+_+"_"+t+"_Container").show(),jQuery("#input_"+_+"_"+t+"_resetbutton").show()})} \ No newline at end of file diff --git a/languages/gravityformssignature.pot b/languages/gravityformssignature.pot new file mode 100644 index 0000000..81ea5cc --- /dev/null +++ b/languages/gravityformssignature.pot @@ -0,0 +1,198 @@ +# Copyright (C) 2023 Gravity Forms +# This file is distributed under the GPL-2.0+. +msgid "" +msgstr "" +"Project-Id-Version: Gravity Forms Signature Add-On 4.6.0\n" +"Report-Msgid-Bugs-To: https://gravityforms.com/support\n" +"Last-Translator: Gravity Forms \n" +"Language-Team: Gravity Forms \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"POT-Creation-Date: 2023-11-16T17:55:21+00:00\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"X-Generator: WP-CLI 2.8.1\n" +"X-Domain: gravityformssignature\n" + +#. Plugin Name of the plugin +msgid "Gravity Forms Signature Add-On" +msgstr "" + +#. Plugin URI of the plugin +#. Author URI of the plugin +msgid "https://gravityforms.com" +msgstr "" + +#. Description of the plugin +msgid "Creates a Gravity Forms signature field that allows users to sign online using a mouse or stylus." +msgstr "" + +#. Author of the plugin +msgid "Gravity Forms" +msgstr "" + +#: class-gf-signature.php:179 +msgid "Reset to re-sign." +msgstr "" + +#: class-gf-signature.php:192 +msgid "Would you like to delete this file? 'Cancel' to stop. 'OK' to delete" +msgstr "" + +#: class-gf-signature.php:246 +#: class-gf-signature.php:271 +msgid "Background Color" +msgstr "" + +#: class-gf-signature.php:246 +msgid "Select the color to be used for the background of the signature area." +msgstr "" + +#: class-gf-signature.php:247 +#: class-gf-signature.php:278 +msgid "Border Color" +msgstr "" + +#: class-gf-signature.php:247 +msgid "Select the color to be used for the border around the signature area." +msgstr "" + +#: class-gf-signature.php:248 +#: class-gf-signature.php:314 +msgid "Pen Color" +msgstr "" + +#: class-gf-signature.php:248 +msgid "Select the color of the pen to be used for the signature." +msgstr "" + +#: class-gf-signature.php:249 +msgid "Width" +msgstr "" + +#: class-gf-signature.php:249 +msgid "Enter the width for the signature area in pixels." +msgstr "" + +#: class-gf-signature.php:250 +#: class-gf-signature.php:297 +msgid "Border Style" +msgstr "" + +#: class-gf-signature.php:250 +msgid "Select the border style to be used around the signature area." +msgstr "" + +#: class-gf-signature.php:251 +#: class-gf-signature.php:321 +msgid "Pen Size" +msgstr "" + +#: class-gf-signature.php:251 +msgid "Select the width of the pen to be used for the signature." +msgstr "" + +#: class-gf-signature.php:252 +#: class-gf-signature.php:285 +msgid "Border Width" +msgstr "" + +#: class-gf-signature.php:252 +msgid "Select the border width to be used around the signature area." +msgstr "" + +#: class-gf-signature.php:253 +msgid "Message" +msgstr "" + +#: class-gf-signature.php:253 +msgid "Write the message you would like to be sent. You can insert fields submitted by the user by selecting them from the 'Insert Variable' drop down." +msgstr "" + +#: class-gf-signature.php:289 +msgid "None" +msgstr "" + +#: class-gf-signature.php:290 +#: class-gf-signature.php:325 +msgid "Small" +msgstr "" + +#: class-gf-signature.php:291 +#: class-gf-signature.php:326 +msgid "Medium" +msgstr "" + +#: class-gf-signature.php:292 +#: class-gf-signature.php:327 +msgid "Large" +msgstr "" + +#: class-gf-signature.php:301 +msgid "Dotted" +msgstr "" + +#: class-gf-signature.php:302 +msgid "Dashed" +msgstr "" + +#: class-gf-signature.php:303 +msgid "Groove" +msgstr "" + +#: class-gf-signature.php:304 +msgid "Ridge" +msgstr "" + +#: class-gf-signature.php:305 +msgid "Inset" +msgstr "" + +#: class-gf-signature.php:306 +msgid "Outset" +msgstr "" + +#: class-gf-signature.php:307 +msgid "Double" +msgstr "" + +#: class-gf-signature.php:308 +msgid "Solid" +msgstr "" + +#: class-gf-signature.php:332 +msgid "Field Width" +msgstr "" + +#: class-gf-signature.php:673 +msgid "There was an issue deleting this signature." +msgstr "" + +#: includes/class-gf-field-signature.php:19 +msgid "Signature" +msgstr "" + +#: includes/class-gf-field-signature.php:43 +msgid "Allows users to sign online using a mouse or stylus." +msgstr "" + +#: includes/class-gf-field-signature.php:210 +msgid "Download file" +msgstr "" + +#: includes/class-gf-field-signature.php:218 +msgid "Delete file" +msgstr "" + +#: includes/class-gf-field-signature.php:226 +msgid "Sign again." +msgstr "" + +#: includes/class-gf-field-signature.php:318 +msgid "Please enter your signature." +msgstr "" + +#: includes/class-gf-field-signature.php:400 +#: includes/class-gf-field-signature.php:422 +msgid "Click to view" +msgstr "" diff --git a/signature.php b/signature.php new file mode 100644 index 0000000..beff717 --- /dev/null +++ b/signature.php @@ -0,0 +1,56 @@ +