Changeset 3494810
- Timestamp:
- 03/30/2026 04:28:22 PM (2 days ago)
- Location:
- pagepin
- Files:
-
- 14 edited
- 1 copied
-
tags/1.0.3 (copied) (copied from pagepin/trunk)
-
tags/1.0.3/admin/class-wizard.php (modified) (14 diffs)
-
tags/1.0.3/assets/css/admin-wizard.css (modified) (1 diff)
-
tags/1.0.3/assets/css/pinpoint.css (modified) (1 diff)
-
tags/1.0.3/assets/js/admin-wizard.js (modified) (1 diff)
-
tags/1.0.3/assets/js/pinpoint.js (modified) (2 diffs)
-
tags/1.0.3/pagepin.php (modified) (16 diffs)
-
tags/1.0.3/readme.txt (modified) (3 diffs)
-
trunk/admin/class-wizard.php (modified) (14 diffs)
-
trunk/assets/css/admin-wizard.css (modified) (1 diff)
-
trunk/assets/css/pinpoint.css (modified) (1 diff)
-
trunk/assets/js/admin-wizard.js (modified) (1 diff)
-
trunk/assets/js/pinpoint.js (modified) (2 diffs)
-
trunk/pagepin.php (modified) (16 diffs)
-
trunk/readme.txt (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
pagepin/tags/1.0.3/admin/class-wizard.php
r3491645 r3494810 38 38 public function __construct() { 39 39 $this->steps = array( 40 'welcome' => array(40 'welcome' => array( 41 41 'name' => __( 'Welcome', 'pagepin' ), 42 42 'handler' => 'render_step_welcome', 43 43 ), 44 'recipients' => array(44 'recipients' => array( 45 45 'name' => __( 'Recipients', 'pagepin' ), 46 46 'handler' => 'render_step_recipients', 47 47 ), 48 'roles' => array(48 'roles' => array( 49 49 'name' => __( 'Roles', 'pagepin' ), 50 50 'handler' => 'render_step_roles', 51 51 ), 52 'public' => array(52 'public' => array( 53 53 'name' => __( 'Public', 'pagepin' ), 54 54 'handler' => 'render_step_public', … … 341 341 </label> 342 342 <p class="pagepin-wizard-toggle-description"> 343 <?php esc_html_e( 'When enabled, any visitor can submit feedback without logging in.', 'pagepin' ); ?>343 <?php esc_html_e( 'When enabled, anyone can submit feedback without a WordPress account.', 'pagepin' ); ?> 344 344 </p> 345 345 </div> … … 384 384 <h2><?php esc_html_e( 'Pinpoint Feature', 'pagepin' ); ?></h2> 385 385 <p class="pagepin-wizard-description"> 386 <?php esc_html_e( 'Pinpoint allows your team to discuss specific elements on any page with threaded comments.', 'pagepin' ); ?>386 <?php esc_html_e( 'Pinpoint lets your team pin comments to specific page elements for focused, threaded discussions.', 'pagepin' ); ?> 387 387 </p> 388 388 … … 395 395 </label> 396 396 <p class="pagepin-wizard-toggle-description"> 397 <?php esc_html_e( 'When enabled, a sidebar will appear on frontend pages for element-based discussions.', 'pagepin' ); ?>397 <?php esc_html_e( 'When enabled, users can pin threaded comments directly to page elements.', 'pagepin' ); ?> 398 398 </p> 399 399 </div> 400 400 401 401 <div class="pagepin-wizard-conditional" id="pagepin-pinpoint-options" style="<?php echo $pinpoint_enabled ? '' : esc_attr( 'display: none;' ); ?>"> 402 <h3 style="margin-top: 20px; margin-bottom: 10px; font-size: 14px; font-weight: 600;">402 <h3> 403 403 <?php esc_html_e( 'Who can create pinpoints?', 'pagepin' ); ?> 404 404 </h3> 405 <div class="pagepin-wizard-roles-grid" style="margin-bottom: 15px;">405 <div class="pagepin-wizard-roles-grid"> 406 406 <?php foreach ( $all_roles as $role_key => $role_name ) : ?> 407 407 <?php $is_checked = in_array( $role_key, (array) $can_create, true ); ?> … … 416 416 </div> 417 417 418 <h3 style="margin-top: 15px; margin-bottom: 10px; font-size: 14px; font-weight: 600;">418 <h3> 419 419 <?php esc_html_e( 'Who can view and comment?', 'pagepin' ); ?> 420 420 </h3> 421 <div class="pagepin-wizard-roles-grid" style="margin-bottom: 15px;">421 <div class="pagepin-wizard-roles-grid"> 422 422 <?php foreach ( $all_roles as $role_key => $role_name ) : ?> 423 423 <?php $is_checked = in_array( $role_key, (array) $can_view, true ); ?> … … 432 432 </div> 433 433 434 <h3 style="margin-top: 15px; margin-bottom: 10px; font-size: 14px; font-weight: 600;">434 <h3> 435 435 <?php esc_html_e( 'Who can resolve and delete?', 'pagepin' ); ?> 436 436 </h3> … … 452 452 <p class="pagepin-wizard-hint"> 453 453 <span class="dashicons dashicons-info-outline"></span> 454 <?php esc_html_e( 'Pinpoint is ideal for internal team discussions about design and content.', 'pagepin' ); ?>454 <?php esc_html_e( 'Pinpoint is ideal for team discussions about design, content, and layout decisions.', 'pagepin' ); ?> 455 455 </p> 456 456 </div> … … 478 478 ?> 479 479 <div class="pagepin-wizard-step-inner"> 480 <h2><?php esc_html_e( 'Extern eCollaborators', 'pagepin' ); ?></h2>480 <h2><?php esc_html_e( 'External Collaborators', 'pagepin' ); ?></h2> 481 481 <p class="pagepin-wizard-description"> 482 <?php esc_html_e( ' Laden Sie externe Personen per E-Mail-Link zur Zusammenarbeit ein — ohne WordPress-Account.', 'pagepin' ); ?>482 <?php esc_html_e( 'Invite external people via email link to collaborate — no WordPress account required.', 'pagepin' ); ?> 483 483 </p> 484 484 … … 488 488 <input type="checkbox" name="enable_collaborators" value="1" <?php checked( $enable_collaborators ); ?> id="pagepin-enable-collaborators" /> 489 489 <span class="pagepin-wizard-toggle-slider"></span> 490 <span class="pagepin-wizard-toggle-label"><?php esc_html_e( 'E xterne Collaborators aktivieren', 'pagepin' ); ?></span>490 <span class="pagepin-wizard-toggle-label"><?php esc_html_e( 'Enable external collaborators', 'pagepin' ); ?></span> 491 491 </label> 492 492 <p class="pagepin-wizard-toggle-description"> 493 <?php esc_html_e( 'W enn aktiviert, können externe Personen ohne WordPress-Account Feedback geben.', 'pagepin' ); ?>493 <?php esc_html_e( 'When enabled, external people can provide feedback without a WordPress account.', 'pagepin' ); ?> 494 494 </p> 495 495 </div> … … 499 499 <input type="checkbox" name="enable_share_links" value="1" <?php checked( $enable_share_links ); ?> /> 500 500 <span class="pagepin-wizard-toggle-slider"></span> 501 <span class="pagepin-wizard-toggle-label"><?php esc_html_e( ' Token-basierte Share-Links aktivieren', 'pagepin' ); ?></span>501 <span class="pagepin-wizard-toggle-label"><?php esc_html_e( 'Enable token-based share links', 'pagepin' ); ?></span> 502 502 </label> 503 503 <p class="pagepin-wizard-toggle-description"> 504 <?php esc_html_e( ' Erzeugt eindeutige Links, die Sie an externe Personen weiterleiten können.', 'pagepin' ); ?>504 <?php esc_html_e( 'Generates unique links that you can share with external people.', 'pagepin' ); ?> 505 505 </p> 506 506 507 <div style="margin-top: 15px;">508 <label for="pagepin-collaborator-link-expiry" style="display: block; margin-bottom: 5px; font-weight: 600; font-size: 13px;">509 <?php esc_html_e( 'Link -Ablauf', 'pagepin' ); ?>507 <div class="pagepin-wizard-conditional-field"> 508 <label for="pagepin-collaborator-link-expiry" class="pagepin-wizard-conditional-label"> 509 <?php esc_html_e( 'Link Expiration', 'pagepin' ); ?> 510 510 </label> 511 <select name="collaborator_link_expiry" id="pagepin-collaborator-link-expiry" class="pagepin-wizard-input " style="width: 100%; max-width: 300px;">512 <option value="0" <?php selected( $link_expiry, 0 ); ?>><?php esc_html_e( 'N ie', 'pagepin' ); ?></option>513 <option value="7" <?php selected( $link_expiry, 7 ); ?>><?php esc_html_e( '7 Tage', 'pagepin' ); ?></option>514 <option value="30" <?php selected( $link_expiry, 30 ); ?>><?php esc_html_e( '30 Tage', 'pagepin' ); ?></option>515 <option value="90" <?php selected( $link_expiry, 90 ); ?>><?php esc_html_e( '90 Tage', 'pagepin' ); ?></option>511 <select name="collaborator_link_expiry" id="pagepin-collaborator-link-expiry" class="pagepin-wizard-input pagepin-wizard-conditional-select"> 512 <option value="0" <?php selected( $link_expiry, 0 ); ?>><?php esc_html_e( 'Never', 'pagepin' ); ?></option> 513 <option value="7" <?php selected( $link_expiry, 7 ); ?>><?php esc_html_e( '7 Days', 'pagepin' ); ?></option> 514 <option value="30" <?php selected( $link_expiry, 30 ); ?>><?php esc_html_e( '30 Days', 'pagepin' ); ?></option> 515 <option value="90" <?php selected( $link_expiry, 90 ); ?>><?php esc_html_e( '90 Days', 'pagepin' ); ?></option> 516 516 </select> 517 517 </div> … … 521 521 <p class="pagepin-wizard-hint"> 522 522 <span class="dashicons dashicons-info-outline"></span> 523 <?php esc_html_e( ' Sie können Collaborators per @email@adresse.de in Pinpoint-Threads einladen.', 'pagepin' ); ?>523 <?php esc_html_e( 'You can invite collaborators via @email in Pinpoint threads.', 'pagepin' ); ?> 524 524 </p> 525 525 </div> … … 540 540 ?> 541 541 <div class="pagepin-wizard-step-inner"> 542 <h2><?php esc_html_e( 'Feedback -Tags', 'pagepin' ); ?></h2>542 <h2><?php esc_html_e( 'Feedback Tags', 'pagepin' ); ?></h2> 543 543 <p class="pagepin-wizard-description"> 544 <?php esc_html_e( 'Organi sieren Sie Feedback und Pinpoints mit farbigen Labels.', 'pagepin' ); ?>544 <?php esc_html_e( 'Organize feedback and pinpoints with color-coded labels.', 'pagepin' ); ?> 545 545 </p> 546 546 … … 550 550 <input type="checkbox" name="enable_tags" value="1" <?php checked( $enable_tags ); ?> id="pagepin-enable-tags" /> 551 551 <span class="pagepin-wizard-toggle-slider"></span> 552 <span class="pagepin-wizard-toggle-label"><?php esc_html_e( ' Tags aktivieren', 'pagepin' ); ?></span>552 <span class="pagepin-wizard-toggle-label"><?php esc_html_e( 'Enable tags', 'pagepin' ); ?></span> 553 553 </label> 554 554 <p class="pagepin-wizard-toggle-description"> 555 <?php esc_html_e( 'W enn aktiviert, können Sie Feedback und Pinpoints mit Tags organisieren.', 'pagepin' ); ?>555 <?php esc_html_e( 'When enabled, you can organize feedback and pinpoints with tags.', 'pagepin' ); ?> 556 556 </p> 557 557 </div> 558 558 559 559 <div class="pagepin-wizard-conditional" id="pagepin-tags-preview" style="<?php echo $enable_tags ? '' : esc_attr( 'display: none;' ); ?>"> 560 <div style="display: flex; flex-wrap: wrap; gap: 8px; margin-top: 15px;">561 <span class="pagepin-wizard-tag-pill" style=" display: inline-block; padding: 4px 12px; border-radius: 12px; font-size: 13px; font-weight: 600; background: #ef4444; color: #ffffff;">562 Bug560 <div class="pagepin-wizard-tag-pills"> 561 <span class="pagepin-wizard-tag-pill" style="background: #ef4444;"> 562 <?php esc_html_e( 'Bug', 'pagepin' ); ?> 563 563 </span> 564 <span class="pagepin-wizard-tag-pill" style=" display: inline-block; padding: 4px 12px; border-radius: 12px; font-size: 13px; font-weight: 600; background: #ff6800; color: #ffffff;">565 Design564 <span class="pagepin-wizard-tag-pill" style="background: #ff6800;"> 565 <?php esc_html_e( 'Design', 'pagepin' ); ?> 566 566 </span> 567 <span class="pagepin-wizard-tag-pill" style=" display: inline-block; padding: 4px 12px; border-radius: 12px; font-size: 13px; font-weight: 600; background: #3b82f6; color: #ffffff;">568 Content567 <span class="pagepin-wizard-tag-pill" style="background: #3b82f6;"> 568 <?php esc_html_e( 'Content', 'pagepin' ); ?> 569 569 </span> 570 <span class="pagepin-wizard-tag-pill" style=" display: inline-block; padding: 4px 12px; border-radius: 12px; font-size: 13px; font-weight: 600; background: #f59e0b; color: #ffffff;">571 Dringend570 <span class="pagepin-wizard-tag-pill" style="background: #f59e0b;"> 571 <?php esc_html_e( 'Urgent', 'pagepin' ); ?> 572 572 </span> 573 573 </div> 574 <p class="pagepin-wizard-toggle-description" style="margin-top: 10px;">575 <?php esc_html_e( ' Diese Standard-Tags werden automatisch erstellt. Weitere Tags können Sie in den Einstellungen hinzufügen.', 'pagepin' ); ?>574 <p class="pagepin-wizard-toggle-description"> 575 <?php esc_html_e( 'These default tags are created automatically. You can add more tags in the settings.', 'pagepin' ); ?> 576 576 </p> 577 577 </div> … … 580 580 <p class="pagepin-wizard-hint"> 581 581 <span class="dashicons dashicons-info-outline"></span> 582 <?php esc_html_e( 'Tags können sowohl Pinshots als auch Pinpoints zugewiesen werden.', 'pagepin' ); ?>582 <?php esc_html_e( 'Tags can be assigned to both Pinshots and Pinpoints.', 'pagepin' ); ?> 583 583 </p> 584 584 </div> -
pagepin/tags/1.0.3/assets/css/admin-wizard.css
r3491645 r3494810 589 589 } 590 590 591 .pagepin-wizard-conditional h3 { 592 color: var(--wizard-slate-50); 593 margin-top: 20px; 594 margin-bottom: 10px; 595 font-size: 14px; 596 font-weight: 600; 597 } 598 599 .pagepin-wizard-conditional h3:first-child { 600 margin-top: 0; 601 } 602 603 .pagepin-wizard-conditional .pagepin-wizard-roles-grid { 604 margin-bottom: 15px; 605 } 606 607 .pagepin-wizard-conditional-field { 608 margin-top: 15px; 609 } 610 611 .pagepin-wizard-conditional-label { 612 display: block; 613 margin-bottom: 5px; 614 font-weight: 600; 615 font-size: 13px; 616 color: var(--wizard-slate-200); 617 } 618 619 .pagepin-wizard-conditional-select { 620 width: 100%; 621 max-width: 300px; 622 } 623 624 .pagepin-wizard-tag-pills { 625 display: flex; 626 flex-wrap: wrap; 627 gap: 8px; 628 margin-top: 15px; 629 } 630 631 .pagepin-wizard-tag-pill { 632 display: inline-block; 633 padding: 4px 12px; 634 border-radius: 12px; 635 font-size: 13px; 636 font-weight: 600; 637 color: #fff; 638 } 639 591 640 /* ===== COMPLETE STEP ===== */ 592 641 .pagepin-wizard-complete { -
pagepin/tags/1.0.3/assets/css/pinpoint.css
r3491645 r3494810 669 669 #pagepin-pinpoint-root .pp-thread-textarea { 670 670 width: 100%; 671 min-height: 60px;671 min-height: 40px; 672 672 max-height: 120px; 673 padding: 10px 12px; 673 padding: 8px 12px; 674 box-sizing: border-box; 674 675 background: var(--pp-color-bg-dark); 675 676 border: 1px solid var(--pp-color-border); -
pagepin/tags/1.0.3/assets/js/admin-wizard.js
r3491645 r3494810 632 632 $btn.removeClass('loading').prop('disabled', false); 633 633 // eslint-disable-next-line no-alert 634 // eslint-disable-next-line no-alert635 634 alert( 636 635 response.data.message || pagepinWizard.strings.error -
pagepin/tags/1.0.3/assets/js/pinpoint.js
r3491645 r3494810 1814 1814 </div> 1815 1815 <div class="pp-thread-input"> 1816 <textarea class="pp-thread-textarea" placeholder="${s.writeComment || 'Write your comment...'}" autofocus></textarea>1816 <textarea class="pp-thread-textarea" rows="2" placeholder="${s.writeComment || 'Write your comment...'}" autofocus></textarea> 1817 1817 <div class="pp-thread-input-actions"> 1818 1818 <span class="pp-thread-hint">${s.mentionHint || '@ to mention'}</span> … … 2179 2179 ? ` 2180 2180 <div class="pp-thread-input"> 2181 <textarea class="pp-thread-textarea" placeholder="${s.writeReply || 'Write a reply...'}"></textarea>2181 <textarea class="pp-thread-textarea" rows="2" placeholder="${s.writeReply || 'Write a reply...'}"></textarea> 2182 2182 <div class="pp-thread-input-actions"> 2183 2183 <span class="pp-thread-hint">${s.mentionHint || '@ to mention'}</span> -
pagepin/tags/1.0.3/pagepin.php
r3491645 r3494810 4 4 * Plugin URI: https://pagepin.io 5 5 * Description: Visual feedback tool for WordPress - collect client feedback with screenshots and annotations. 6 * Version: 1.0. 26 * Version: 1.0.3 7 7 * Author: Patrick Schlesinger 8 8 * Author URI: https://pagepin.io … … 25 25 } 26 26 27 define( 'PAGEPIN_VERSION', '1.0. 2' );27 define( 'PAGEPIN_VERSION', '1.0.3' ); 28 28 define( 'PAGEPIN_PLUGIN_FILE', __FILE__ ); 29 29 define( 'PAGEPIN_PLUGIN_PATH', plugin_dir_path( __FILE__ ) ); … … 835 835 836 836 $localize_data = array( 837 'ajaxurl' => admin_url( 'admin-ajax.php' ),838 'nonce' => wp_create_nonce( 'pagepin_pinpoint_nonce' ),839 'adminNonce' => wp_create_nonce( 'pagepin_nonce' ),840 'enabled' => true,841 'enableTags' => ! empty( $options['enable_tags'] ),842 'canCreate' => $can_create,843 'canView' => $can_view,844 'canViewOwn' => ! $is_collaborator && $can_create && ! $this->current_user_can_pinpoint( 'view' ),845 'canResolve' => $is_collaborator ? false : $this->current_user_can_pinpoint( 'resolve' ),846 'currentUser' => get_current_user_id(),837 'ajaxurl' => admin_url( 'admin-ajax.php' ), 838 'nonce' => wp_create_nonce( 'pagepin_pinpoint_nonce' ), 839 'adminNonce' => wp_create_nonce( 'pagepin_nonce' ), 840 'enabled' => true, 841 'enableTags' => ! empty( $options['enable_tags'] ), 842 'canCreate' => $can_create, 843 'canView' => $can_view, 844 'canViewOwn' => ! $is_collaborator && $can_create && ! $this->current_user_can_pinpoint( 'view' ), 845 'canResolve' => $is_collaborator ? false : $this->current_user_can_pinpoint( 'resolve' ), 846 'currentUser' => get_current_user_id(), 847 847 ); 848 848 849 849 // Add collaborator context if active. 850 850 if ( $is_collaborator && $collaborator ) { 851 $collab_db = new PagePin_Collaborator_Database();852 $accessible = $collab_db->get_accessible_pinpoints( $collaborator->id );853 $collab_nonce = wp_create_nonce( 'pagepin_collaborator_nonce' );851 $collab_db = new PagePin_Collaborator_Database(); 852 $accessible = $collab_db->get_accessible_pinpoints( $collaborator->id ); 853 $collab_nonce = wp_create_nonce( 'pagepin_collaborator_nonce' ); 854 854 855 855 $localize_data['isCollaborator'] = true; 856 $localize_data['collaboratorName'] = $collaborator->display_name;857 $localize_data['collaboratorNonce'] = $collab_nonce;858 $localize_data['accessiblePinpoints'] = $accessible;856 $localize_data['collaboratorName'] = $collaborator->display_name; 857 $localize_data['collaboratorNonce'] = $collab_nonce; 858 $localize_data['accessiblePinpoints'] = $accessible; 859 859 } else { 860 860 $localize_data['isCollaborator'] = false; 861 $localize_data['enableCollaborators'] = ! empty( $options['enable_collaborators'] );862 $localize_data['enableShareLinks'] = ! empty( $options['enable_share_links'] );861 $localize_data['enableCollaborators'] = ! empty( $options['enable_collaborators'] ); 862 $localize_data['enableShareLinks'] = ! empty( $options['enable_share_links'] ); 863 863 } 864 864 865 865 $localize_data['strings'] = array( 866 'pinpoints'=> __( 'Pinpoints', 'pagepin' ),867 'addPinpoint'=> __( 'Add Pinpoint', 'pagepin' ),868 'toggle'=> __( 'Toggle sidebar', 'pagepin' ),869 'filterOpen'=> __( 'Open', 'pagepin' ),870 'filterResolved'=> __( 'Resolved', 'pagepin' ),871 'filterAll'=> __( 'All', 'pagepin' ),872 'noPinpoints'=> __( 'No pinpoints yet', 'pagepin' ),873 'noPinpointsDesc'=> __( 'Click "Add Pinpoint" to start a discussion on any element.', 'pagepin' ),874 'selectElement'=> __( 'Click on an element to add a pinpoint', 'pagepin' ),875 'newPinpoint'=> __( 'New Pinpoint', 'pagepin' ),876 'writeComment'=> __( 'Write your comment...', 'pagepin' ),877 'writeReply'=> __( 'Write a reply...', 'pagepin' ),878 'mentionHint'=> __( '@ to mention', 'pagepin' ),879 'create'=> __( 'Create', 'pagepin' ),880 'reply'=> __( 'Reply', 'pagepin' ),881 'close'=> __( 'Close', 'pagepin' ),882 'resolve'=> __( 'Resolve', 'pagepin' ),883 'reopen'=> __( 'Reopen', 'pagepin' ),884 'delete'=> __( 'Delete', 'pagepin' ),885 'resolved'=> __( 'Resolved', 'pagepin' ),886 'open'=> __( 'Open', 'pagepin' ),887 'resolvedBy'=> __( 'Resolved', 'pagepin' ),888 'noComments'=> __( 'No comments yet', 'pagepin' ),889 'pinpointCreated'=> __( 'Pinpoint created successfully', 'pagepin' ),890 'pinpointResolved'=> __( 'Pinpoint resolved', 'pagepin' ),891 'pinpointReopened'=> __( 'Pinpoint reopened', 'pagepin' ),892 'pinpointDeleted'=> __( 'Pinpoint deleted', 'pagepin' ),893 'confirmDelete'=> __( 'Are you sure you want to delete this pinpoint?', 'pagepin' ),894 'error'=> __( 'An error occurred', 'pagepin' ),895 'loadError'=> __( 'Error loading pinpoints', 'pagepin' ),896 'tryAgain'=> __( 'Please try refreshing the page.', 'pagepin' ),897 'justNow'=> __( 'just now', 'pagepin' ),898 'comment'=> __( 'comment', 'pagepin' ),899 'comments'=> __( 'comments', 'pagepin' ),900 'guest'=> __( 'Guest', 'pagepin' ),901 'disconnect'=> __( 'Disconnect', 'pagepin' ),902 'inviteEmail'=> __( 'Invite', 'pagepin' ),903 'sharePinpoint'=> __( 'Share Pinpoint', 'pagepin' ),904 'internalLink'=> __( 'Internal Link', 'pagepin' ),905 'internalLinkHint'=> __( 'For logged-in team members — opens the pinpoint sidebar directly.', 'pagepin' ),906 'shareExternally'=> __( 'Share Externally', 'pagepin' ),907 'existingGuest'=> __( 'Existing guest:', 'pagepin' ),908 'selectGuest'=> __( 'Select guest...', 'pagepin' ),909 'grantAccess'=> __( 'Grant Access', 'pagepin' ),910 'orCreateNewGuest'=> __( '— or create new guest —', 'pagepin' ),911 'guestName'=> __( 'Guest name', 'pagepin' ),912 'emailOptional'=> __( 'Email', 'pagepin' ),913 'optional'=> __( 'optional', 'pagepin' ),914 'inviteAndCreate'=> __( 'Invite & Create Link', 'pagepin' ),915 'copy'=> __( 'Copy', 'pagepin' ),916 'copied'=> __( 'Copied!', 'pagepin' ),917 'accessGranted'=> __( 'Access granted', 'pagepin' ),918 'accessRevoked'=> __( 'Access revoked', 'pagepin' ),919 'nameRequired'=> __( 'Name is required', 'pagepin' ),920 'failedToCreate'=> __( 'Failed to create', 'pagepin' ),921 'failedToGrant'=> __( 'Failed to grant access', 'pagepin' ),922 'failedToRevoke'=> __( 'Failed to revoke', 'pagepin' ),923 'invitationSent'=> __( 'Invitation sent to', 'pagepin' ),924 'shareLinkCreated'=> __( 'Share link created for', 'pagepin' ),925 'activeExternalAccess' => __( 'Active External Access', 'pagepin' ),926 'noEmail'=> __( 'no email', 'pagepin' ),927 'revoke'=> __( 'Revoke', 'pagepin' ),928 'copyLink'=> __( 'Copy Link', 'pagepin' ),929 'name'=> __( 'Name:', 'pagepin' ),866 'pinpoints' => __( 'Pinpoints', 'pagepin' ), 867 'addPinpoint' => __( 'Add Pinpoint', 'pagepin' ), 868 'toggle' => __( 'Toggle sidebar', 'pagepin' ), 869 'filterOpen' => __( 'Open', 'pagepin' ), 870 'filterResolved' => __( 'Resolved', 'pagepin' ), 871 'filterAll' => __( 'All', 'pagepin' ), 872 'noPinpoints' => __( 'No pinpoints yet', 'pagepin' ), 873 'noPinpointsDesc' => __( 'Click "Add Pinpoint" to start a discussion on any element.', 'pagepin' ), 874 'selectElement' => __( 'Click on an element to add a pinpoint', 'pagepin' ), 875 'newPinpoint' => __( 'New Pinpoint', 'pagepin' ), 876 'writeComment' => __( 'Write your comment...', 'pagepin' ), 877 'writeReply' => __( 'Write a reply...', 'pagepin' ), 878 'mentionHint' => __( '@ to mention', 'pagepin' ), 879 'create' => __( 'Create', 'pagepin' ), 880 'reply' => __( 'Reply', 'pagepin' ), 881 'close' => __( 'Close', 'pagepin' ), 882 'resolve' => __( 'Resolve', 'pagepin' ), 883 'reopen' => __( 'Reopen', 'pagepin' ), 884 'delete' => __( 'Delete', 'pagepin' ), 885 'resolved' => __( 'Resolved', 'pagepin' ), 886 'open' => __( 'Open', 'pagepin' ), 887 'resolvedBy' => __( 'Resolved', 'pagepin' ), 888 'noComments' => __( 'No comments yet', 'pagepin' ), 889 'pinpointCreated' => __( 'Pinpoint created successfully', 'pagepin' ), 890 'pinpointResolved' => __( 'Pinpoint resolved', 'pagepin' ), 891 'pinpointReopened' => __( 'Pinpoint reopened', 'pagepin' ), 892 'pinpointDeleted' => __( 'Pinpoint deleted', 'pagepin' ), 893 'confirmDelete' => __( 'Are you sure you want to delete this pinpoint?', 'pagepin' ), 894 'error' => __( 'An error occurred', 'pagepin' ), 895 'loadError' => __( 'Error loading pinpoints', 'pagepin' ), 896 'tryAgain' => __( 'Please try refreshing the page.', 'pagepin' ), 897 'justNow' => __( 'just now', 'pagepin' ), 898 'comment' => __( 'comment', 'pagepin' ), 899 'comments' => __( 'comments', 'pagepin' ), 900 'guest' => __( 'Guest', 'pagepin' ), 901 'disconnect' => __( 'Disconnect', 'pagepin' ), 902 'inviteEmail' => __( 'Invite', 'pagepin' ), 903 'sharePinpoint' => __( 'Share Pinpoint', 'pagepin' ), 904 'internalLink' => __( 'Internal Link', 'pagepin' ), 905 'internalLinkHint' => __( 'For logged-in team members — opens the pinpoint sidebar directly.', 'pagepin' ), 906 'shareExternally' => __( 'Share Externally', 'pagepin' ), 907 'existingGuest' => __( 'Existing guest:', 'pagepin' ), 908 'selectGuest' => __( 'Select guest...', 'pagepin' ), 909 'grantAccess' => __( 'Grant Access', 'pagepin' ), 910 'orCreateNewGuest' => __( '— or create new guest —', 'pagepin' ), 911 'guestName' => __( 'Guest name', 'pagepin' ), 912 'emailOptional' => __( 'Email', 'pagepin' ), 913 'optional' => __( 'optional', 'pagepin' ), 914 'inviteAndCreate' => __( 'Invite & Create Link', 'pagepin' ), 915 'copy' => __( 'Copy', 'pagepin' ), 916 'copied' => __( 'Copied!', 'pagepin' ), 917 'accessGranted' => __( 'Access granted', 'pagepin' ), 918 'accessRevoked' => __( 'Access revoked', 'pagepin' ), 919 'nameRequired' => __( 'Name is required', 'pagepin' ), 920 'failedToCreate' => __( 'Failed to create', 'pagepin' ), 921 'failedToGrant' => __( 'Failed to grant access', 'pagepin' ), 922 'failedToRevoke' => __( 'Failed to revoke', 'pagepin' ), 923 'invitationSent' => __( 'Invitation sent to', 'pagepin' ), 924 'shareLinkCreated' => __( 'Share link created for', 'pagepin' ), 925 'activeExternalAccess' => __( 'Active External Access', 'pagepin' ), 926 'noEmail' => __( 'no email', 'pagepin' ), 927 'revoke' => __( 'Revoke', 'pagepin' ), 928 'copyLink' => __( 'Copy Link', 'pagepin' ), 929 'name' => __( 'Name:', 'pagepin' ), 930 930 ); 931 931 … … 1108 1108 set_transient( $rate_limit_key, $submission_count + 1, HOUR_IN_SECONDS ); 1109 1109 1110 $screenshot_data = isset( $_POST['screenshot'] ) ? wp_unslash( $_POST['screenshot'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Base64 screenshot data, validated in save_screenshot().1110 $screenshot_data = isset( $_POST['screenshot'] ) ? wp_unslash( $_POST['screenshot'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Base64 screenshot data, validated in save_screenshot(). 1111 1111 $feedback_data_json = isset( $_POST['feedback_data'] ) ? wp_unslash( $_POST['feedback_data'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- JSON data, validated after decode. 1112 1112 $page_url = isset( $_POST['page_url'] ) ? esc_url_raw( wp_unslash( $_POST['page_url'] ) ) : ''; … … 1656 1656 'adminUrl' => admin_url( 'admin.php?page=pagepin' ), 1657 1657 'strings' => array( 1658 'next' => __( 'Next', 'pagepin' ),1659 'finish' => __( 'Finish Setup', 'pagepin' ),1660 'error' => __( 'An error occurred. Please try again.', 'pagepin' ),1661 'emailRequired' => __( 'Please enter at least one email address.', 'pagepin' ),1662 'emailInvalid' => __( 'Please enter a valid email address.', 'pagepin' ),1663 'roleRequired' => __( 'Please select at least one user role.', 'pagepin' ),1664 'namePlaceholder' => __( 'Name (optional)', 'pagepin' ),1665 'emailPlaceholder' => __( 'Email Address', 'pagepin' ),1666 'remove' => __( 'Remove', 'pagepin' ),1667 'summaryRecipients' => __( 'Email Recipients', 'pagepin' ),1668 'summaryRoles' => __( 'User Roles', 'pagepin' ),1669 'summaryPublic' => __( 'Public Feedback', 'pagepin' ),1670 'summaryPinpoint' => __( 'Pinpoint', 'pagepin' ),1658 'next' => __( 'Next', 'pagepin' ), 1659 'finish' => __( 'Finish Setup', 'pagepin' ), 1660 'error' => __( 'An error occurred. Please try again.', 'pagepin' ), 1661 'emailRequired' => __( 'Please enter at least one email address.', 'pagepin' ), 1662 'emailInvalid' => __( 'Please enter a valid email address.', 'pagepin' ), 1663 'roleRequired' => __( 'Please select at least one user role.', 'pagepin' ), 1664 'namePlaceholder' => __( 'Name (optional)', 'pagepin' ), 1665 'emailPlaceholder' => __( 'Email Address', 'pagepin' ), 1666 'remove' => __( 'Remove', 'pagepin' ), 1667 'summaryRecipients' => __( 'Email Recipients', 'pagepin' ), 1668 'summaryRoles' => __( 'User Roles', 'pagepin' ), 1669 'summaryPublic' => __( 'Public Feedback', 'pagepin' ), 1670 'summaryPinpoint' => __( 'Pinpoint', 'pagepin' ), 1671 1671 'summaryCollaborators' => __( 'Collaborators', 'pagepin' ), 1672 1672 'summaryTags' => __( 'Tags', 'pagepin' ), … … 1750 1750 1751 1751 if ( isset( $form_data['collaborator_link_expiry'] ) ) { 1752 $expiry = absint( $form_data['collaborator_link_expiry'] );1753 $allowed_values = array( 0, 7, 30, 90 );1752 $expiry = absint( $form_data['collaborator_link_expiry'] ); 1753 $allowed_values = array( 0, 7, 30, 90 ); 1754 1754 $options['collaborator_link_expiry'] = in_array( $expiry, $allowed_values, true ) ? $expiry : 0; 1755 1755 } … … 2438 2438 2439 2439 if ( isset( $input['collaborator_link_expiry'] ) ) { 2440 $expiry = absint( $input['collaborator_link_expiry'] );2441 $allowed_values = array( 0, 7, 30, 90 );2440 $expiry = absint( $input['collaborator_link_expiry'] ); 2441 $allowed_values = array( 0, 7, 30, 90 ); 2442 2442 $sanitized['collaborator_link_expiry'] = in_array( $expiry, $allowed_values, true ) ? $expiry : 0; 2443 2443 } … … 2651 2651 } 2652 2652 2653 $screenshot_data = isset( $_POST['screenshot'] ) ? wp_unslash( $_POST['screenshot'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Base64 screenshot data, validated in save_screenshot().2653 $screenshot_data = isset( $_POST['screenshot'] ) ? wp_unslash( $_POST['screenshot'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Base64 screenshot data, validated in save_screenshot(). 2654 2654 $feedback_data_json = isset( $_POST['feedback_data'] ) ? wp_unslash( $_POST['feedback_data'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- JSON data, validated after decode. 2655 2655 $page_url = isset( $_POST['page_url'] ) ? esc_url_raw( wp_unslash( $_POST['page_url'] ) ) : ''; … … 2810 2810 2811 2811 if ( ! empty( $mentions ) ) { 2812 $email_handler = new PagePin_Pinpoint_Email();2813 2812 $pinpoint = $database->get( $pinpoint_id ); 2814 2813 if ( $pinpoint && ! empty( $pinpoint->comments ) ) { 2815 2814 $comment_id = $pinpoint->comments[0]->id; 2816 $user_ids = array(); 2817 foreach ( $mentions as $username ) { 2818 $user = get_user_by( 'login', $username ); 2819 if ( $user ) { 2820 $user_ids[] = $user->ID; 2821 } 2822 } 2823 if ( ! empty( $user_ids ) ) { 2824 $email_handler->send_mention_notification( $pinpoint_id, $comment_id, $user_ids ); 2825 } 2815 $this->process_mentions( $mentions, $pinpoint_id, $comment_id ); 2826 2816 } 2827 2817 } … … 2910 2900 // Include share links if enabled. 2911 2901 if ( ! empty( $options['enable_share_links'] ) ) { 2912 $collab_db = new PagePin_Collaborator_Database();2902 $collab_db = new PagePin_Collaborator_Database(); 2913 2903 $pinpoint->share_links = $collab_db->get_share_links_for_pinpoint( $pinpoint_id ); 2914 2904 } … … 2959 2949 } 2960 2950 2951 $share_links_created = $this->process_mentions( $mentions, $pinpoint_id, $comment_id ); 2952 2961 2953 $email_handler = new PagePin_Pinpoint_Email(); 2962 2963 $share_links_created = array();2964 2965 if ( ! empty( $mentions ) ) {2966 $user_ids = array();2967 $collaborator_ids = array();2968 $collab_db = new PagePin_Collaborator_Database();2969 foreach ( $mentions as $mention ) {2970 // Check if this is a share-link mention (!@name).2971 if ( str_starts_with( $mention, '!' ) ) {2972 $share_name = substr( $mention, 1 );2973 if ( ! empty( $share_name ) ) {2974 $share_url = $this->handle_share_link_mention( $share_name, $pinpoint_id );2975 if ( $share_url ) {2976 $share_links_created[] = array(2977 'name' => $share_name,2978 'url' => $share_url,2979 );2980 }2981 }2982 continue;2983 }2984 // Check if this is a collaborator mention (collab_X).2985 if ( str_starts_with( $mention, 'collab_' ) ) {2986 $collab_id = absint( substr( $mention, 7 ) );2987 if ( $collab_id > 0 ) {2988 // Auto-grant access if collaborator doesn't have it yet.2989 if ( ! $collab_db->has_access( $collab_id, $pinpoint_id ) ) {2990 $collab_db->grant_access( $collab_id, $pinpoint_id, get_current_user_id() );2991 }2992 $collaborator_ids[] = $collab_id;2993 }2994 continue;2995 }2996 // Check if this is an email invite mention.2997 if ( is_email( $mention ) ) {2998 $this->handle_email_mention( $mention, $pinpoint_id );2999 continue;3000 }3001 $user = get_user_by( 'login', $mention );3002 if ( $user ) {3003 $user_ids[] = $user->ID;3004 }3005 }3006 if ( ! empty( $user_ids ) ) {3007 $email_handler->send_mention_notification( $pinpoint_id, $comment_id, $user_ids );3008 }3009 if ( ! empty( $collaborator_ids ) ) {3010 $email_handler->send_collaborator_mention_notification( $pinpoint_id, $comment_id, $collaborator_ids );3011 }3012 }3013 3014 2954 $email_handler->send_thread_notification( $pinpoint_id, $comment_id ); 3015 2955 … … 3038 2978 $display_name = ucfirst( $email_parts[0] ); 3039 2979 $this->handle_email_collaborator_creation( $email, $display_name, $pinpoint_id ); 2980 } 2981 2982 /** 2983 * Process mentions from a comment: send notifications, grant access, create share links. 2984 * 2985 * @since 1.0.3 2986 * @param array $mentions Array of mention strings extracted from comment text. 2987 * @param int $pinpoint_id The pinpoint ID. 2988 * @param int $comment_id The comment ID. 2989 * @return array Share links created (array of name/url pairs). 2990 */ 2991 private function process_mentions( $mentions, $pinpoint_id, $comment_id ) { 2992 if ( empty( $mentions ) || ! is_array( $mentions ) ) { 2993 return array(); 2994 } 2995 2996 $mentions = array_unique( $mentions ); 2997 $user_ids = array(); 2998 $collaborator_ids = array(); 2999 $share_links_created = array(); 3000 $collab_db = new PagePin_Collaborator_Database(); 3001 $current_user_id = get_current_user_id(); 3002 3003 foreach ( $mentions as $mention ) { 3004 // Share-link mention (!@name). 3005 if ( str_starts_with( $mention, '!' ) ) { 3006 $share_name = substr( $mention, 1 ); 3007 if ( ! empty( $share_name ) ) { 3008 $share_url = $this->handle_share_link_mention( $share_name, $pinpoint_id ); 3009 if ( $share_url ) { 3010 $share_links_created[] = array( 3011 'name' => $share_name, 3012 'url' => $share_url, 3013 ); 3014 } 3015 } 3016 continue; 3017 } 3018 // Collaborator mention (collab_X). 3019 if ( str_starts_with( $mention, 'collab_' ) ) { 3020 $collab_id = absint( substr( $mention, 7 ) ); 3021 if ( $collab_id > 0 ) { 3022 if ( ! $collab_db->has_access( $collab_id, $pinpoint_id ) ) { 3023 $collab_db->grant_access( $collab_id, $pinpoint_id, $current_user_id ); 3024 } 3025 $collaborator_ids[] = $collab_id; 3026 } 3027 continue; 3028 } 3029 // Email invite mention. 3030 if ( is_email( $mention ) ) { 3031 $this->handle_email_mention( $mention, $pinpoint_id ); 3032 continue; 3033 } 3034 // WordPress user mention. 3035 $user = get_user_by( 'login', $mention ); 3036 if ( $user && absint( $user->ID ) !== $current_user_id ) { 3037 $user_ids[] = $user->ID; 3038 } 3039 } 3040 3041 $email_handler = new PagePin_Pinpoint_Email(); 3042 if ( ! empty( $user_ids ) ) { 3043 $email_handler->send_mention_notification( $pinpoint_id, $comment_id, $user_ids ); 3044 } 3045 if ( ! empty( $collaborator_ids ) ) { 3046 $email_handler->send_collaborator_mention_notification( $pinpoint_id, $comment_id, $collaborator_ids ); 3047 } 3048 3049 return $share_links_created; 3040 3050 } 3041 3051 … … 3465 3475 $collab_db = new PagePin_Collaborator_Database(); 3466 3476 $collaborators = $collab_db->search_collaborators( $search ); 3477 $guest_avatar = get_avatar_url( 0, array( 'size' => 32 ) ); 3467 3478 3468 3479 foreach ( $collaborators as $collab ) { 3469 $avatar_email = ! empty( $collab->email ) ? $collab->email : '';3470 3480 $display_info = ! empty( $collab->email ) ? $collab->email : __( 'Guest', 'pagepin' ); 3471 3481 … … 3475 3485 'login' => 'collab_' . $collab->id, 3476 3486 'display_login' => $display_info, 3477 'avatar' => get_avatar_url( $avatar_email, array( 'size' => 32 ) ),3487 'avatar' => $guest_avatar, 3478 3488 'is_collaborator' => true, 3479 3489 ); … … 3488 3498 if ( ! $existing_user && ! $existing_collab ) { 3489 3499 $users[] = array( 3490 'id' => 'invite_' . $search,3491 'name' => __( 'Invite', 'pagepin' ) . ': ' . $search,3492 'login' => $search,3493 'avatar' => get_avatar_url( $search, array( 'size' => 32 ) ),3494 'is_invite' => true,3500 'id' => 'invite_' . $search, 3501 'name' => __( 'Invite', 'pagepin' ) . ': ' . $search, 3502 'login' => $search, 3503 'avatar' => get_avatar_url( $search, array( 'size' => 32 ) ), 3504 'is_invite' => true, 3495 3505 ); 3496 3506 } … … 4089 4099 if ( ! isset( $options['enable_tags'] ) ) { 4090 4100 $options['enable_tags'] = true; 4091 $changed = true;4101 $changed = true; 4092 4102 } 4093 4103 if ( ! isset( $options['admin_bar_enabled'] ) ) { -
pagepin/tags/1.0.3/readme.txt
r3494254 r3494810 6 6 Tested up to: 6.9 7 7 Requires PHP: 8.1 8 Stable tag: 1.0. 28 Stable tag: 1.0.3 9 9 License: GPLv2 or later 10 10 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 214 214 215 215 == Changelog == 216 217 = 1.0.3 = 218 * Improvement: Setup wizard fully localized in English with translation support 219 * Fix: Contrast issue with Pinpoint role headings in setup wizard 220 * Fix: Duplicate @mention notifications prevented 221 * Fix: Self-mention no longer triggers email notification 222 * Fix: Collaborator and email mentions now work in initial pinpoint comments 223 * Fix: Guest collaborators show generic avatar in mention autocomplete 224 * Fix: Pinpoint reply textarea sizing and overflow 225 * Improvement: Inline styles moved to CSS classes for better maintainability 226 * Improvement: Refined wizard step descriptions for clarity 216 227 217 228 = 1.0.2 = … … 248 259 == Upgrade Notice == 249 260 261 = 1.0.3 = 262 Setup wizard fully English and translatable. Fixed duplicate mention notifications, self-mentions, and collaborator mentions in initial pinpoints. 263 250 264 = 1.0.2 = 251 265 New collaboration features: Tag system, external collaborators via Magic Link, shareable feedback links, collaborator reuse, @mention notifications, and admin bar integration. -
pagepin/trunk/admin/class-wizard.php
r3491645 r3494810 38 38 public function __construct() { 39 39 $this->steps = array( 40 'welcome' => array(40 'welcome' => array( 41 41 'name' => __( 'Welcome', 'pagepin' ), 42 42 'handler' => 'render_step_welcome', 43 43 ), 44 'recipients' => array(44 'recipients' => array( 45 45 'name' => __( 'Recipients', 'pagepin' ), 46 46 'handler' => 'render_step_recipients', 47 47 ), 48 'roles' => array(48 'roles' => array( 49 49 'name' => __( 'Roles', 'pagepin' ), 50 50 'handler' => 'render_step_roles', 51 51 ), 52 'public' => array(52 'public' => array( 53 53 'name' => __( 'Public', 'pagepin' ), 54 54 'handler' => 'render_step_public', … … 341 341 </label> 342 342 <p class="pagepin-wizard-toggle-description"> 343 <?php esc_html_e( 'When enabled, any visitor can submit feedback without logging in.', 'pagepin' ); ?>343 <?php esc_html_e( 'When enabled, anyone can submit feedback without a WordPress account.', 'pagepin' ); ?> 344 344 </p> 345 345 </div> … … 384 384 <h2><?php esc_html_e( 'Pinpoint Feature', 'pagepin' ); ?></h2> 385 385 <p class="pagepin-wizard-description"> 386 <?php esc_html_e( 'Pinpoint allows your team to discuss specific elements on any page with threaded comments.', 'pagepin' ); ?>386 <?php esc_html_e( 'Pinpoint lets your team pin comments to specific page elements for focused, threaded discussions.', 'pagepin' ); ?> 387 387 </p> 388 388 … … 395 395 </label> 396 396 <p class="pagepin-wizard-toggle-description"> 397 <?php esc_html_e( 'When enabled, a sidebar will appear on frontend pages for element-based discussions.', 'pagepin' ); ?>397 <?php esc_html_e( 'When enabled, users can pin threaded comments directly to page elements.', 'pagepin' ); ?> 398 398 </p> 399 399 </div> 400 400 401 401 <div class="pagepin-wizard-conditional" id="pagepin-pinpoint-options" style="<?php echo $pinpoint_enabled ? '' : esc_attr( 'display: none;' ); ?>"> 402 <h3 style="margin-top: 20px; margin-bottom: 10px; font-size: 14px; font-weight: 600;">402 <h3> 403 403 <?php esc_html_e( 'Who can create pinpoints?', 'pagepin' ); ?> 404 404 </h3> 405 <div class="pagepin-wizard-roles-grid" style="margin-bottom: 15px;">405 <div class="pagepin-wizard-roles-grid"> 406 406 <?php foreach ( $all_roles as $role_key => $role_name ) : ?> 407 407 <?php $is_checked = in_array( $role_key, (array) $can_create, true ); ?> … … 416 416 </div> 417 417 418 <h3 style="margin-top: 15px; margin-bottom: 10px; font-size: 14px; font-weight: 600;">418 <h3> 419 419 <?php esc_html_e( 'Who can view and comment?', 'pagepin' ); ?> 420 420 </h3> 421 <div class="pagepin-wizard-roles-grid" style="margin-bottom: 15px;">421 <div class="pagepin-wizard-roles-grid"> 422 422 <?php foreach ( $all_roles as $role_key => $role_name ) : ?> 423 423 <?php $is_checked = in_array( $role_key, (array) $can_view, true ); ?> … … 432 432 </div> 433 433 434 <h3 style="margin-top: 15px; margin-bottom: 10px; font-size: 14px; font-weight: 600;">434 <h3> 435 435 <?php esc_html_e( 'Who can resolve and delete?', 'pagepin' ); ?> 436 436 </h3> … … 452 452 <p class="pagepin-wizard-hint"> 453 453 <span class="dashicons dashicons-info-outline"></span> 454 <?php esc_html_e( 'Pinpoint is ideal for internal team discussions about design and content.', 'pagepin' ); ?>454 <?php esc_html_e( 'Pinpoint is ideal for team discussions about design, content, and layout decisions.', 'pagepin' ); ?> 455 455 </p> 456 456 </div> … … 478 478 ?> 479 479 <div class="pagepin-wizard-step-inner"> 480 <h2><?php esc_html_e( 'Extern eCollaborators', 'pagepin' ); ?></h2>480 <h2><?php esc_html_e( 'External Collaborators', 'pagepin' ); ?></h2> 481 481 <p class="pagepin-wizard-description"> 482 <?php esc_html_e( ' Laden Sie externe Personen per E-Mail-Link zur Zusammenarbeit ein — ohne WordPress-Account.', 'pagepin' ); ?>482 <?php esc_html_e( 'Invite external people via email link to collaborate — no WordPress account required.', 'pagepin' ); ?> 483 483 </p> 484 484 … … 488 488 <input type="checkbox" name="enable_collaborators" value="1" <?php checked( $enable_collaborators ); ?> id="pagepin-enable-collaborators" /> 489 489 <span class="pagepin-wizard-toggle-slider"></span> 490 <span class="pagepin-wizard-toggle-label"><?php esc_html_e( 'E xterne Collaborators aktivieren', 'pagepin' ); ?></span>490 <span class="pagepin-wizard-toggle-label"><?php esc_html_e( 'Enable external collaborators', 'pagepin' ); ?></span> 491 491 </label> 492 492 <p class="pagepin-wizard-toggle-description"> 493 <?php esc_html_e( 'W enn aktiviert, können externe Personen ohne WordPress-Account Feedback geben.', 'pagepin' ); ?>493 <?php esc_html_e( 'When enabled, external people can provide feedback without a WordPress account.', 'pagepin' ); ?> 494 494 </p> 495 495 </div> … … 499 499 <input type="checkbox" name="enable_share_links" value="1" <?php checked( $enable_share_links ); ?> /> 500 500 <span class="pagepin-wizard-toggle-slider"></span> 501 <span class="pagepin-wizard-toggle-label"><?php esc_html_e( ' Token-basierte Share-Links aktivieren', 'pagepin' ); ?></span>501 <span class="pagepin-wizard-toggle-label"><?php esc_html_e( 'Enable token-based share links', 'pagepin' ); ?></span> 502 502 </label> 503 503 <p class="pagepin-wizard-toggle-description"> 504 <?php esc_html_e( ' Erzeugt eindeutige Links, die Sie an externe Personen weiterleiten können.', 'pagepin' ); ?>504 <?php esc_html_e( 'Generates unique links that you can share with external people.', 'pagepin' ); ?> 505 505 </p> 506 506 507 <div style="margin-top: 15px;">508 <label for="pagepin-collaborator-link-expiry" style="display: block; margin-bottom: 5px; font-weight: 600; font-size: 13px;">509 <?php esc_html_e( 'Link -Ablauf', 'pagepin' ); ?>507 <div class="pagepin-wizard-conditional-field"> 508 <label for="pagepin-collaborator-link-expiry" class="pagepin-wizard-conditional-label"> 509 <?php esc_html_e( 'Link Expiration', 'pagepin' ); ?> 510 510 </label> 511 <select name="collaborator_link_expiry" id="pagepin-collaborator-link-expiry" class="pagepin-wizard-input " style="width: 100%; max-width: 300px;">512 <option value="0" <?php selected( $link_expiry, 0 ); ?>><?php esc_html_e( 'N ie', 'pagepin' ); ?></option>513 <option value="7" <?php selected( $link_expiry, 7 ); ?>><?php esc_html_e( '7 Tage', 'pagepin' ); ?></option>514 <option value="30" <?php selected( $link_expiry, 30 ); ?>><?php esc_html_e( '30 Tage', 'pagepin' ); ?></option>515 <option value="90" <?php selected( $link_expiry, 90 ); ?>><?php esc_html_e( '90 Tage', 'pagepin' ); ?></option>511 <select name="collaborator_link_expiry" id="pagepin-collaborator-link-expiry" class="pagepin-wizard-input pagepin-wizard-conditional-select"> 512 <option value="0" <?php selected( $link_expiry, 0 ); ?>><?php esc_html_e( 'Never', 'pagepin' ); ?></option> 513 <option value="7" <?php selected( $link_expiry, 7 ); ?>><?php esc_html_e( '7 Days', 'pagepin' ); ?></option> 514 <option value="30" <?php selected( $link_expiry, 30 ); ?>><?php esc_html_e( '30 Days', 'pagepin' ); ?></option> 515 <option value="90" <?php selected( $link_expiry, 90 ); ?>><?php esc_html_e( '90 Days', 'pagepin' ); ?></option> 516 516 </select> 517 517 </div> … … 521 521 <p class="pagepin-wizard-hint"> 522 522 <span class="dashicons dashicons-info-outline"></span> 523 <?php esc_html_e( ' Sie können Collaborators per @email@adresse.de in Pinpoint-Threads einladen.', 'pagepin' ); ?>523 <?php esc_html_e( 'You can invite collaborators via @email in Pinpoint threads.', 'pagepin' ); ?> 524 524 </p> 525 525 </div> … … 540 540 ?> 541 541 <div class="pagepin-wizard-step-inner"> 542 <h2><?php esc_html_e( 'Feedback -Tags', 'pagepin' ); ?></h2>542 <h2><?php esc_html_e( 'Feedback Tags', 'pagepin' ); ?></h2> 543 543 <p class="pagepin-wizard-description"> 544 <?php esc_html_e( 'Organi sieren Sie Feedback und Pinpoints mit farbigen Labels.', 'pagepin' ); ?>544 <?php esc_html_e( 'Organize feedback and pinpoints with color-coded labels.', 'pagepin' ); ?> 545 545 </p> 546 546 … … 550 550 <input type="checkbox" name="enable_tags" value="1" <?php checked( $enable_tags ); ?> id="pagepin-enable-tags" /> 551 551 <span class="pagepin-wizard-toggle-slider"></span> 552 <span class="pagepin-wizard-toggle-label"><?php esc_html_e( ' Tags aktivieren', 'pagepin' ); ?></span>552 <span class="pagepin-wizard-toggle-label"><?php esc_html_e( 'Enable tags', 'pagepin' ); ?></span> 553 553 </label> 554 554 <p class="pagepin-wizard-toggle-description"> 555 <?php esc_html_e( 'W enn aktiviert, können Sie Feedback und Pinpoints mit Tags organisieren.', 'pagepin' ); ?>555 <?php esc_html_e( 'When enabled, you can organize feedback and pinpoints with tags.', 'pagepin' ); ?> 556 556 </p> 557 557 </div> 558 558 559 559 <div class="pagepin-wizard-conditional" id="pagepin-tags-preview" style="<?php echo $enable_tags ? '' : esc_attr( 'display: none;' ); ?>"> 560 <div style="display: flex; flex-wrap: wrap; gap: 8px; margin-top: 15px;">561 <span class="pagepin-wizard-tag-pill" style=" display: inline-block; padding: 4px 12px; border-radius: 12px; font-size: 13px; font-weight: 600; background: #ef4444; color: #ffffff;">562 Bug560 <div class="pagepin-wizard-tag-pills"> 561 <span class="pagepin-wizard-tag-pill" style="background: #ef4444;"> 562 <?php esc_html_e( 'Bug', 'pagepin' ); ?> 563 563 </span> 564 <span class="pagepin-wizard-tag-pill" style=" display: inline-block; padding: 4px 12px; border-radius: 12px; font-size: 13px; font-weight: 600; background: #ff6800; color: #ffffff;">565 Design564 <span class="pagepin-wizard-tag-pill" style="background: #ff6800;"> 565 <?php esc_html_e( 'Design', 'pagepin' ); ?> 566 566 </span> 567 <span class="pagepin-wizard-tag-pill" style=" display: inline-block; padding: 4px 12px; border-radius: 12px; font-size: 13px; font-weight: 600; background: #3b82f6; color: #ffffff;">568 Content567 <span class="pagepin-wizard-tag-pill" style="background: #3b82f6;"> 568 <?php esc_html_e( 'Content', 'pagepin' ); ?> 569 569 </span> 570 <span class="pagepin-wizard-tag-pill" style=" display: inline-block; padding: 4px 12px; border-radius: 12px; font-size: 13px; font-weight: 600; background: #f59e0b; color: #ffffff;">571 Dringend570 <span class="pagepin-wizard-tag-pill" style="background: #f59e0b;"> 571 <?php esc_html_e( 'Urgent', 'pagepin' ); ?> 572 572 </span> 573 573 </div> 574 <p class="pagepin-wizard-toggle-description" style="margin-top: 10px;">575 <?php esc_html_e( ' Diese Standard-Tags werden automatisch erstellt. Weitere Tags können Sie in den Einstellungen hinzufügen.', 'pagepin' ); ?>574 <p class="pagepin-wizard-toggle-description"> 575 <?php esc_html_e( 'These default tags are created automatically. You can add more tags in the settings.', 'pagepin' ); ?> 576 576 </p> 577 577 </div> … … 580 580 <p class="pagepin-wizard-hint"> 581 581 <span class="dashicons dashicons-info-outline"></span> 582 <?php esc_html_e( 'Tags können sowohl Pinshots als auch Pinpoints zugewiesen werden.', 'pagepin' ); ?>582 <?php esc_html_e( 'Tags can be assigned to both Pinshots and Pinpoints.', 'pagepin' ); ?> 583 583 </p> 584 584 </div> -
pagepin/trunk/assets/css/admin-wizard.css
r3491645 r3494810 589 589 } 590 590 591 .pagepin-wizard-conditional h3 { 592 color: var(--wizard-slate-50); 593 margin-top: 20px; 594 margin-bottom: 10px; 595 font-size: 14px; 596 font-weight: 600; 597 } 598 599 .pagepin-wizard-conditional h3:first-child { 600 margin-top: 0; 601 } 602 603 .pagepin-wizard-conditional .pagepin-wizard-roles-grid { 604 margin-bottom: 15px; 605 } 606 607 .pagepin-wizard-conditional-field { 608 margin-top: 15px; 609 } 610 611 .pagepin-wizard-conditional-label { 612 display: block; 613 margin-bottom: 5px; 614 font-weight: 600; 615 font-size: 13px; 616 color: var(--wizard-slate-200); 617 } 618 619 .pagepin-wizard-conditional-select { 620 width: 100%; 621 max-width: 300px; 622 } 623 624 .pagepin-wizard-tag-pills { 625 display: flex; 626 flex-wrap: wrap; 627 gap: 8px; 628 margin-top: 15px; 629 } 630 631 .pagepin-wizard-tag-pill { 632 display: inline-block; 633 padding: 4px 12px; 634 border-radius: 12px; 635 font-size: 13px; 636 font-weight: 600; 637 color: #fff; 638 } 639 591 640 /* ===== COMPLETE STEP ===== */ 592 641 .pagepin-wizard-complete { -
pagepin/trunk/assets/css/pinpoint.css
r3491645 r3494810 669 669 #pagepin-pinpoint-root .pp-thread-textarea { 670 670 width: 100%; 671 min-height: 60px;671 min-height: 40px; 672 672 max-height: 120px; 673 padding: 10px 12px; 673 padding: 8px 12px; 674 box-sizing: border-box; 674 675 background: var(--pp-color-bg-dark); 675 676 border: 1px solid var(--pp-color-border); -
pagepin/trunk/assets/js/admin-wizard.js
r3491645 r3494810 632 632 $btn.removeClass('loading').prop('disabled', false); 633 633 // eslint-disable-next-line no-alert 634 // eslint-disable-next-line no-alert635 634 alert( 636 635 response.data.message || pagepinWizard.strings.error -
pagepin/trunk/assets/js/pinpoint.js
r3491645 r3494810 1814 1814 </div> 1815 1815 <div class="pp-thread-input"> 1816 <textarea class="pp-thread-textarea" placeholder="${s.writeComment || 'Write your comment...'}" autofocus></textarea>1816 <textarea class="pp-thread-textarea" rows="2" placeholder="${s.writeComment || 'Write your comment...'}" autofocus></textarea> 1817 1817 <div class="pp-thread-input-actions"> 1818 1818 <span class="pp-thread-hint">${s.mentionHint || '@ to mention'}</span> … … 2179 2179 ? ` 2180 2180 <div class="pp-thread-input"> 2181 <textarea class="pp-thread-textarea" placeholder="${s.writeReply || 'Write a reply...'}"></textarea>2181 <textarea class="pp-thread-textarea" rows="2" placeholder="${s.writeReply || 'Write a reply...'}"></textarea> 2182 2182 <div class="pp-thread-input-actions"> 2183 2183 <span class="pp-thread-hint">${s.mentionHint || '@ to mention'}</span> -
pagepin/trunk/pagepin.php
r3491645 r3494810 4 4 * Plugin URI: https://pagepin.io 5 5 * Description: Visual feedback tool for WordPress - collect client feedback with screenshots and annotations. 6 * Version: 1.0. 26 * Version: 1.0.3 7 7 * Author: Patrick Schlesinger 8 8 * Author URI: https://pagepin.io … … 25 25 } 26 26 27 define( 'PAGEPIN_VERSION', '1.0. 2' );27 define( 'PAGEPIN_VERSION', '1.0.3' ); 28 28 define( 'PAGEPIN_PLUGIN_FILE', __FILE__ ); 29 29 define( 'PAGEPIN_PLUGIN_PATH', plugin_dir_path( __FILE__ ) ); … … 835 835 836 836 $localize_data = array( 837 'ajaxurl' => admin_url( 'admin-ajax.php' ),838 'nonce' => wp_create_nonce( 'pagepin_pinpoint_nonce' ),839 'adminNonce' => wp_create_nonce( 'pagepin_nonce' ),840 'enabled' => true,841 'enableTags' => ! empty( $options['enable_tags'] ),842 'canCreate' => $can_create,843 'canView' => $can_view,844 'canViewOwn' => ! $is_collaborator && $can_create && ! $this->current_user_can_pinpoint( 'view' ),845 'canResolve' => $is_collaborator ? false : $this->current_user_can_pinpoint( 'resolve' ),846 'currentUser' => get_current_user_id(),837 'ajaxurl' => admin_url( 'admin-ajax.php' ), 838 'nonce' => wp_create_nonce( 'pagepin_pinpoint_nonce' ), 839 'adminNonce' => wp_create_nonce( 'pagepin_nonce' ), 840 'enabled' => true, 841 'enableTags' => ! empty( $options['enable_tags'] ), 842 'canCreate' => $can_create, 843 'canView' => $can_view, 844 'canViewOwn' => ! $is_collaborator && $can_create && ! $this->current_user_can_pinpoint( 'view' ), 845 'canResolve' => $is_collaborator ? false : $this->current_user_can_pinpoint( 'resolve' ), 846 'currentUser' => get_current_user_id(), 847 847 ); 848 848 849 849 // Add collaborator context if active. 850 850 if ( $is_collaborator && $collaborator ) { 851 $collab_db = new PagePin_Collaborator_Database();852 $accessible = $collab_db->get_accessible_pinpoints( $collaborator->id );853 $collab_nonce = wp_create_nonce( 'pagepin_collaborator_nonce' );851 $collab_db = new PagePin_Collaborator_Database(); 852 $accessible = $collab_db->get_accessible_pinpoints( $collaborator->id ); 853 $collab_nonce = wp_create_nonce( 'pagepin_collaborator_nonce' ); 854 854 855 855 $localize_data['isCollaborator'] = true; 856 $localize_data['collaboratorName'] = $collaborator->display_name;857 $localize_data['collaboratorNonce'] = $collab_nonce;858 $localize_data['accessiblePinpoints'] = $accessible;856 $localize_data['collaboratorName'] = $collaborator->display_name; 857 $localize_data['collaboratorNonce'] = $collab_nonce; 858 $localize_data['accessiblePinpoints'] = $accessible; 859 859 } else { 860 860 $localize_data['isCollaborator'] = false; 861 $localize_data['enableCollaborators'] = ! empty( $options['enable_collaborators'] );862 $localize_data['enableShareLinks'] = ! empty( $options['enable_share_links'] );861 $localize_data['enableCollaborators'] = ! empty( $options['enable_collaborators'] ); 862 $localize_data['enableShareLinks'] = ! empty( $options['enable_share_links'] ); 863 863 } 864 864 865 865 $localize_data['strings'] = array( 866 'pinpoints'=> __( 'Pinpoints', 'pagepin' ),867 'addPinpoint'=> __( 'Add Pinpoint', 'pagepin' ),868 'toggle'=> __( 'Toggle sidebar', 'pagepin' ),869 'filterOpen'=> __( 'Open', 'pagepin' ),870 'filterResolved'=> __( 'Resolved', 'pagepin' ),871 'filterAll'=> __( 'All', 'pagepin' ),872 'noPinpoints'=> __( 'No pinpoints yet', 'pagepin' ),873 'noPinpointsDesc'=> __( 'Click "Add Pinpoint" to start a discussion on any element.', 'pagepin' ),874 'selectElement'=> __( 'Click on an element to add a pinpoint', 'pagepin' ),875 'newPinpoint'=> __( 'New Pinpoint', 'pagepin' ),876 'writeComment'=> __( 'Write your comment...', 'pagepin' ),877 'writeReply'=> __( 'Write a reply...', 'pagepin' ),878 'mentionHint'=> __( '@ to mention', 'pagepin' ),879 'create'=> __( 'Create', 'pagepin' ),880 'reply'=> __( 'Reply', 'pagepin' ),881 'close'=> __( 'Close', 'pagepin' ),882 'resolve'=> __( 'Resolve', 'pagepin' ),883 'reopen'=> __( 'Reopen', 'pagepin' ),884 'delete'=> __( 'Delete', 'pagepin' ),885 'resolved'=> __( 'Resolved', 'pagepin' ),886 'open'=> __( 'Open', 'pagepin' ),887 'resolvedBy'=> __( 'Resolved', 'pagepin' ),888 'noComments'=> __( 'No comments yet', 'pagepin' ),889 'pinpointCreated'=> __( 'Pinpoint created successfully', 'pagepin' ),890 'pinpointResolved'=> __( 'Pinpoint resolved', 'pagepin' ),891 'pinpointReopened'=> __( 'Pinpoint reopened', 'pagepin' ),892 'pinpointDeleted'=> __( 'Pinpoint deleted', 'pagepin' ),893 'confirmDelete'=> __( 'Are you sure you want to delete this pinpoint?', 'pagepin' ),894 'error'=> __( 'An error occurred', 'pagepin' ),895 'loadError'=> __( 'Error loading pinpoints', 'pagepin' ),896 'tryAgain'=> __( 'Please try refreshing the page.', 'pagepin' ),897 'justNow'=> __( 'just now', 'pagepin' ),898 'comment'=> __( 'comment', 'pagepin' ),899 'comments'=> __( 'comments', 'pagepin' ),900 'guest'=> __( 'Guest', 'pagepin' ),901 'disconnect'=> __( 'Disconnect', 'pagepin' ),902 'inviteEmail'=> __( 'Invite', 'pagepin' ),903 'sharePinpoint'=> __( 'Share Pinpoint', 'pagepin' ),904 'internalLink'=> __( 'Internal Link', 'pagepin' ),905 'internalLinkHint'=> __( 'For logged-in team members — opens the pinpoint sidebar directly.', 'pagepin' ),906 'shareExternally'=> __( 'Share Externally', 'pagepin' ),907 'existingGuest'=> __( 'Existing guest:', 'pagepin' ),908 'selectGuest'=> __( 'Select guest...', 'pagepin' ),909 'grantAccess'=> __( 'Grant Access', 'pagepin' ),910 'orCreateNewGuest'=> __( '— or create new guest —', 'pagepin' ),911 'guestName'=> __( 'Guest name', 'pagepin' ),912 'emailOptional'=> __( 'Email', 'pagepin' ),913 'optional'=> __( 'optional', 'pagepin' ),914 'inviteAndCreate'=> __( 'Invite & Create Link', 'pagepin' ),915 'copy'=> __( 'Copy', 'pagepin' ),916 'copied'=> __( 'Copied!', 'pagepin' ),917 'accessGranted'=> __( 'Access granted', 'pagepin' ),918 'accessRevoked'=> __( 'Access revoked', 'pagepin' ),919 'nameRequired'=> __( 'Name is required', 'pagepin' ),920 'failedToCreate'=> __( 'Failed to create', 'pagepin' ),921 'failedToGrant'=> __( 'Failed to grant access', 'pagepin' ),922 'failedToRevoke'=> __( 'Failed to revoke', 'pagepin' ),923 'invitationSent'=> __( 'Invitation sent to', 'pagepin' ),924 'shareLinkCreated'=> __( 'Share link created for', 'pagepin' ),925 'activeExternalAccess' => __( 'Active External Access', 'pagepin' ),926 'noEmail'=> __( 'no email', 'pagepin' ),927 'revoke'=> __( 'Revoke', 'pagepin' ),928 'copyLink'=> __( 'Copy Link', 'pagepin' ),929 'name'=> __( 'Name:', 'pagepin' ),866 'pinpoints' => __( 'Pinpoints', 'pagepin' ), 867 'addPinpoint' => __( 'Add Pinpoint', 'pagepin' ), 868 'toggle' => __( 'Toggle sidebar', 'pagepin' ), 869 'filterOpen' => __( 'Open', 'pagepin' ), 870 'filterResolved' => __( 'Resolved', 'pagepin' ), 871 'filterAll' => __( 'All', 'pagepin' ), 872 'noPinpoints' => __( 'No pinpoints yet', 'pagepin' ), 873 'noPinpointsDesc' => __( 'Click "Add Pinpoint" to start a discussion on any element.', 'pagepin' ), 874 'selectElement' => __( 'Click on an element to add a pinpoint', 'pagepin' ), 875 'newPinpoint' => __( 'New Pinpoint', 'pagepin' ), 876 'writeComment' => __( 'Write your comment...', 'pagepin' ), 877 'writeReply' => __( 'Write a reply...', 'pagepin' ), 878 'mentionHint' => __( '@ to mention', 'pagepin' ), 879 'create' => __( 'Create', 'pagepin' ), 880 'reply' => __( 'Reply', 'pagepin' ), 881 'close' => __( 'Close', 'pagepin' ), 882 'resolve' => __( 'Resolve', 'pagepin' ), 883 'reopen' => __( 'Reopen', 'pagepin' ), 884 'delete' => __( 'Delete', 'pagepin' ), 885 'resolved' => __( 'Resolved', 'pagepin' ), 886 'open' => __( 'Open', 'pagepin' ), 887 'resolvedBy' => __( 'Resolved', 'pagepin' ), 888 'noComments' => __( 'No comments yet', 'pagepin' ), 889 'pinpointCreated' => __( 'Pinpoint created successfully', 'pagepin' ), 890 'pinpointResolved' => __( 'Pinpoint resolved', 'pagepin' ), 891 'pinpointReopened' => __( 'Pinpoint reopened', 'pagepin' ), 892 'pinpointDeleted' => __( 'Pinpoint deleted', 'pagepin' ), 893 'confirmDelete' => __( 'Are you sure you want to delete this pinpoint?', 'pagepin' ), 894 'error' => __( 'An error occurred', 'pagepin' ), 895 'loadError' => __( 'Error loading pinpoints', 'pagepin' ), 896 'tryAgain' => __( 'Please try refreshing the page.', 'pagepin' ), 897 'justNow' => __( 'just now', 'pagepin' ), 898 'comment' => __( 'comment', 'pagepin' ), 899 'comments' => __( 'comments', 'pagepin' ), 900 'guest' => __( 'Guest', 'pagepin' ), 901 'disconnect' => __( 'Disconnect', 'pagepin' ), 902 'inviteEmail' => __( 'Invite', 'pagepin' ), 903 'sharePinpoint' => __( 'Share Pinpoint', 'pagepin' ), 904 'internalLink' => __( 'Internal Link', 'pagepin' ), 905 'internalLinkHint' => __( 'For logged-in team members — opens the pinpoint sidebar directly.', 'pagepin' ), 906 'shareExternally' => __( 'Share Externally', 'pagepin' ), 907 'existingGuest' => __( 'Existing guest:', 'pagepin' ), 908 'selectGuest' => __( 'Select guest...', 'pagepin' ), 909 'grantAccess' => __( 'Grant Access', 'pagepin' ), 910 'orCreateNewGuest' => __( '— or create new guest —', 'pagepin' ), 911 'guestName' => __( 'Guest name', 'pagepin' ), 912 'emailOptional' => __( 'Email', 'pagepin' ), 913 'optional' => __( 'optional', 'pagepin' ), 914 'inviteAndCreate' => __( 'Invite & Create Link', 'pagepin' ), 915 'copy' => __( 'Copy', 'pagepin' ), 916 'copied' => __( 'Copied!', 'pagepin' ), 917 'accessGranted' => __( 'Access granted', 'pagepin' ), 918 'accessRevoked' => __( 'Access revoked', 'pagepin' ), 919 'nameRequired' => __( 'Name is required', 'pagepin' ), 920 'failedToCreate' => __( 'Failed to create', 'pagepin' ), 921 'failedToGrant' => __( 'Failed to grant access', 'pagepin' ), 922 'failedToRevoke' => __( 'Failed to revoke', 'pagepin' ), 923 'invitationSent' => __( 'Invitation sent to', 'pagepin' ), 924 'shareLinkCreated' => __( 'Share link created for', 'pagepin' ), 925 'activeExternalAccess' => __( 'Active External Access', 'pagepin' ), 926 'noEmail' => __( 'no email', 'pagepin' ), 927 'revoke' => __( 'Revoke', 'pagepin' ), 928 'copyLink' => __( 'Copy Link', 'pagepin' ), 929 'name' => __( 'Name:', 'pagepin' ), 930 930 ); 931 931 … … 1108 1108 set_transient( $rate_limit_key, $submission_count + 1, HOUR_IN_SECONDS ); 1109 1109 1110 $screenshot_data = isset( $_POST['screenshot'] ) ? wp_unslash( $_POST['screenshot'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Base64 screenshot data, validated in save_screenshot().1110 $screenshot_data = isset( $_POST['screenshot'] ) ? wp_unslash( $_POST['screenshot'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Base64 screenshot data, validated in save_screenshot(). 1111 1111 $feedback_data_json = isset( $_POST['feedback_data'] ) ? wp_unslash( $_POST['feedback_data'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- JSON data, validated after decode. 1112 1112 $page_url = isset( $_POST['page_url'] ) ? esc_url_raw( wp_unslash( $_POST['page_url'] ) ) : ''; … … 1656 1656 'adminUrl' => admin_url( 'admin.php?page=pagepin' ), 1657 1657 'strings' => array( 1658 'next' => __( 'Next', 'pagepin' ),1659 'finish' => __( 'Finish Setup', 'pagepin' ),1660 'error' => __( 'An error occurred. Please try again.', 'pagepin' ),1661 'emailRequired' => __( 'Please enter at least one email address.', 'pagepin' ),1662 'emailInvalid' => __( 'Please enter a valid email address.', 'pagepin' ),1663 'roleRequired' => __( 'Please select at least one user role.', 'pagepin' ),1664 'namePlaceholder' => __( 'Name (optional)', 'pagepin' ),1665 'emailPlaceholder' => __( 'Email Address', 'pagepin' ),1666 'remove' => __( 'Remove', 'pagepin' ),1667 'summaryRecipients' => __( 'Email Recipients', 'pagepin' ),1668 'summaryRoles' => __( 'User Roles', 'pagepin' ),1669 'summaryPublic' => __( 'Public Feedback', 'pagepin' ),1670 'summaryPinpoint' => __( 'Pinpoint', 'pagepin' ),1658 'next' => __( 'Next', 'pagepin' ), 1659 'finish' => __( 'Finish Setup', 'pagepin' ), 1660 'error' => __( 'An error occurred. Please try again.', 'pagepin' ), 1661 'emailRequired' => __( 'Please enter at least one email address.', 'pagepin' ), 1662 'emailInvalid' => __( 'Please enter a valid email address.', 'pagepin' ), 1663 'roleRequired' => __( 'Please select at least one user role.', 'pagepin' ), 1664 'namePlaceholder' => __( 'Name (optional)', 'pagepin' ), 1665 'emailPlaceholder' => __( 'Email Address', 'pagepin' ), 1666 'remove' => __( 'Remove', 'pagepin' ), 1667 'summaryRecipients' => __( 'Email Recipients', 'pagepin' ), 1668 'summaryRoles' => __( 'User Roles', 'pagepin' ), 1669 'summaryPublic' => __( 'Public Feedback', 'pagepin' ), 1670 'summaryPinpoint' => __( 'Pinpoint', 'pagepin' ), 1671 1671 'summaryCollaborators' => __( 'Collaborators', 'pagepin' ), 1672 1672 'summaryTags' => __( 'Tags', 'pagepin' ), … … 1750 1750 1751 1751 if ( isset( $form_data['collaborator_link_expiry'] ) ) { 1752 $expiry = absint( $form_data['collaborator_link_expiry'] );1753 $allowed_values = array( 0, 7, 30, 90 );1752 $expiry = absint( $form_data['collaborator_link_expiry'] ); 1753 $allowed_values = array( 0, 7, 30, 90 ); 1754 1754 $options['collaborator_link_expiry'] = in_array( $expiry, $allowed_values, true ) ? $expiry : 0; 1755 1755 } … … 2438 2438 2439 2439 if ( isset( $input['collaborator_link_expiry'] ) ) { 2440 $expiry = absint( $input['collaborator_link_expiry'] );2441 $allowed_values = array( 0, 7, 30, 90 );2440 $expiry = absint( $input['collaborator_link_expiry'] ); 2441 $allowed_values = array( 0, 7, 30, 90 ); 2442 2442 $sanitized['collaborator_link_expiry'] = in_array( $expiry, $allowed_values, true ) ? $expiry : 0; 2443 2443 } … … 2651 2651 } 2652 2652 2653 $screenshot_data = isset( $_POST['screenshot'] ) ? wp_unslash( $_POST['screenshot'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Base64 screenshot data, validated in save_screenshot().2653 $screenshot_data = isset( $_POST['screenshot'] ) ? wp_unslash( $_POST['screenshot'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Base64 screenshot data, validated in save_screenshot(). 2654 2654 $feedback_data_json = isset( $_POST['feedback_data'] ) ? wp_unslash( $_POST['feedback_data'] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- JSON data, validated after decode. 2655 2655 $page_url = isset( $_POST['page_url'] ) ? esc_url_raw( wp_unslash( $_POST['page_url'] ) ) : ''; … … 2810 2810 2811 2811 if ( ! empty( $mentions ) ) { 2812 $email_handler = new PagePin_Pinpoint_Email();2813 2812 $pinpoint = $database->get( $pinpoint_id ); 2814 2813 if ( $pinpoint && ! empty( $pinpoint->comments ) ) { 2815 2814 $comment_id = $pinpoint->comments[0]->id; 2816 $user_ids = array(); 2817 foreach ( $mentions as $username ) { 2818 $user = get_user_by( 'login', $username ); 2819 if ( $user ) { 2820 $user_ids[] = $user->ID; 2821 } 2822 } 2823 if ( ! empty( $user_ids ) ) { 2824 $email_handler->send_mention_notification( $pinpoint_id, $comment_id, $user_ids ); 2825 } 2815 $this->process_mentions( $mentions, $pinpoint_id, $comment_id ); 2826 2816 } 2827 2817 } … … 2910 2900 // Include share links if enabled. 2911 2901 if ( ! empty( $options['enable_share_links'] ) ) { 2912 $collab_db = new PagePin_Collaborator_Database();2902 $collab_db = new PagePin_Collaborator_Database(); 2913 2903 $pinpoint->share_links = $collab_db->get_share_links_for_pinpoint( $pinpoint_id ); 2914 2904 } … … 2959 2949 } 2960 2950 2951 $share_links_created = $this->process_mentions( $mentions, $pinpoint_id, $comment_id ); 2952 2961 2953 $email_handler = new PagePin_Pinpoint_Email(); 2962 2963 $share_links_created = array();2964 2965 if ( ! empty( $mentions ) ) {2966 $user_ids = array();2967 $collaborator_ids = array();2968 $collab_db = new PagePin_Collaborator_Database();2969 foreach ( $mentions as $mention ) {2970 // Check if this is a share-link mention (!@name).2971 if ( str_starts_with( $mention, '!' ) ) {2972 $share_name = substr( $mention, 1 );2973 if ( ! empty( $share_name ) ) {2974 $share_url = $this->handle_share_link_mention( $share_name, $pinpoint_id );2975 if ( $share_url ) {2976 $share_links_created[] = array(2977 'name' => $share_name,2978 'url' => $share_url,2979 );2980 }2981 }2982 continue;2983 }2984 // Check if this is a collaborator mention (collab_X).2985 if ( str_starts_with( $mention, 'collab_' ) ) {2986 $collab_id = absint( substr( $mention, 7 ) );2987 if ( $collab_id > 0 ) {2988 // Auto-grant access if collaborator doesn't have it yet.2989 if ( ! $collab_db->has_access( $collab_id, $pinpoint_id ) ) {2990 $collab_db->grant_access( $collab_id, $pinpoint_id, get_current_user_id() );2991 }2992 $collaborator_ids[] = $collab_id;2993 }2994 continue;2995 }2996 // Check if this is an email invite mention.2997 if ( is_email( $mention ) ) {2998 $this->handle_email_mention( $mention, $pinpoint_id );2999 continue;3000 }3001 $user = get_user_by( 'login', $mention );3002 if ( $user ) {3003 $user_ids[] = $user->ID;3004 }3005 }3006 if ( ! empty( $user_ids ) ) {3007 $email_handler->send_mention_notification( $pinpoint_id, $comment_id, $user_ids );3008 }3009 if ( ! empty( $collaborator_ids ) ) {3010 $email_handler->send_collaborator_mention_notification( $pinpoint_id, $comment_id, $collaborator_ids );3011 }3012 }3013 3014 2954 $email_handler->send_thread_notification( $pinpoint_id, $comment_id ); 3015 2955 … … 3038 2978 $display_name = ucfirst( $email_parts[0] ); 3039 2979 $this->handle_email_collaborator_creation( $email, $display_name, $pinpoint_id ); 2980 } 2981 2982 /** 2983 * Process mentions from a comment: send notifications, grant access, create share links. 2984 * 2985 * @since 1.0.3 2986 * @param array $mentions Array of mention strings extracted from comment text. 2987 * @param int $pinpoint_id The pinpoint ID. 2988 * @param int $comment_id The comment ID. 2989 * @return array Share links created (array of name/url pairs). 2990 */ 2991 private function process_mentions( $mentions, $pinpoint_id, $comment_id ) { 2992 if ( empty( $mentions ) || ! is_array( $mentions ) ) { 2993 return array(); 2994 } 2995 2996 $mentions = array_unique( $mentions ); 2997 $user_ids = array(); 2998 $collaborator_ids = array(); 2999 $share_links_created = array(); 3000 $collab_db = new PagePin_Collaborator_Database(); 3001 $current_user_id = get_current_user_id(); 3002 3003 foreach ( $mentions as $mention ) { 3004 // Share-link mention (!@name). 3005 if ( str_starts_with( $mention, '!' ) ) { 3006 $share_name = substr( $mention, 1 ); 3007 if ( ! empty( $share_name ) ) { 3008 $share_url = $this->handle_share_link_mention( $share_name, $pinpoint_id ); 3009 if ( $share_url ) { 3010 $share_links_created[] = array( 3011 'name' => $share_name, 3012 'url' => $share_url, 3013 ); 3014 } 3015 } 3016 continue; 3017 } 3018 // Collaborator mention (collab_X). 3019 if ( str_starts_with( $mention, 'collab_' ) ) { 3020 $collab_id = absint( substr( $mention, 7 ) ); 3021 if ( $collab_id > 0 ) { 3022 if ( ! $collab_db->has_access( $collab_id, $pinpoint_id ) ) { 3023 $collab_db->grant_access( $collab_id, $pinpoint_id, $current_user_id ); 3024 } 3025 $collaborator_ids[] = $collab_id; 3026 } 3027 continue; 3028 } 3029 // Email invite mention. 3030 if ( is_email( $mention ) ) { 3031 $this->handle_email_mention( $mention, $pinpoint_id ); 3032 continue; 3033 } 3034 // WordPress user mention. 3035 $user = get_user_by( 'login', $mention ); 3036 if ( $user && absint( $user->ID ) !== $current_user_id ) { 3037 $user_ids[] = $user->ID; 3038 } 3039 } 3040 3041 $email_handler = new PagePin_Pinpoint_Email(); 3042 if ( ! empty( $user_ids ) ) { 3043 $email_handler->send_mention_notification( $pinpoint_id, $comment_id, $user_ids ); 3044 } 3045 if ( ! empty( $collaborator_ids ) ) { 3046 $email_handler->send_collaborator_mention_notification( $pinpoint_id, $comment_id, $collaborator_ids ); 3047 } 3048 3049 return $share_links_created; 3040 3050 } 3041 3051 … … 3465 3475 $collab_db = new PagePin_Collaborator_Database(); 3466 3476 $collaborators = $collab_db->search_collaborators( $search ); 3477 $guest_avatar = get_avatar_url( 0, array( 'size' => 32 ) ); 3467 3478 3468 3479 foreach ( $collaborators as $collab ) { 3469 $avatar_email = ! empty( $collab->email ) ? $collab->email : '';3470 3480 $display_info = ! empty( $collab->email ) ? $collab->email : __( 'Guest', 'pagepin' ); 3471 3481 … … 3475 3485 'login' => 'collab_' . $collab->id, 3476 3486 'display_login' => $display_info, 3477 'avatar' => get_avatar_url( $avatar_email, array( 'size' => 32 ) ),3487 'avatar' => $guest_avatar, 3478 3488 'is_collaborator' => true, 3479 3489 ); … … 3488 3498 if ( ! $existing_user && ! $existing_collab ) { 3489 3499 $users[] = array( 3490 'id' => 'invite_' . $search,3491 'name' => __( 'Invite', 'pagepin' ) . ': ' . $search,3492 'login' => $search,3493 'avatar' => get_avatar_url( $search, array( 'size' => 32 ) ),3494 'is_invite' => true,3500 'id' => 'invite_' . $search, 3501 'name' => __( 'Invite', 'pagepin' ) . ': ' . $search, 3502 'login' => $search, 3503 'avatar' => get_avatar_url( $search, array( 'size' => 32 ) ), 3504 'is_invite' => true, 3495 3505 ); 3496 3506 } … … 4089 4099 if ( ! isset( $options['enable_tags'] ) ) { 4090 4100 $options['enable_tags'] = true; 4091 $changed = true;4101 $changed = true; 4092 4102 } 4093 4103 if ( ! isset( $options['admin_bar_enabled'] ) ) { -
pagepin/trunk/readme.txt
r3494254 r3494810 6 6 Tested up to: 6.9 7 7 Requires PHP: 8.1 8 Stable tag: 1.0. 28 Stable tag: 1.0.3 9 9 License: GPLv2 or later 10 10 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 214 214 215 215 == Changelog == 216 217 = 1.0.3 = 218 * Improvement: Setup wizard fully localized in English with translation support 219 * Fix: Contrast issue with Pinpoint role headings in setup wizard 220 * Fix: Duplicate @mention notifications prevented 221 * Fix: Self-mention no longer triggers email notification 222 * Fix: Collaborator and email mentions now work in initial pinpoint comments 223 * Fix: Guest collaborators show generic avatar in mention autocomplete 224 * Fix: Pinpoint reply textarea sizing and overflow 225 * Improvement: Inline styles moved to CSS classes for better maintainability 226 * Improvement: Refined wizard step descriptions for clarity 216 227 217 228 = 1.0.2 = … … 248 259 == Upgrade Notice == 249 260 261 = 1.0.3 = 262 Setup wizard fully English and translatable. Fixed duplicate mention notifications, self-mentions, and collaborator mentions in initial pinpoints. 263 250 264 = 1.0.2 = 251 265 New collaboration features: Tag system, external collaborators via Magic Link, shareable feedback links, collaborator reuse, @mention notifications, and admin bar integration.
Note: See TracChangeset
for help on using the changeset viewer.