Skip to content

Commit

Permalink
Merge branch 'master' of github.com:freescout-helpdesk/freescout into…
Browse files Browse the repository at this point in the history
… dist
  • Loading branch information
freescout-help-desk committed Dec 20, 2024
2 parents b564d20 + 18fb794 commit 178ac6d
Show file tree
Hide file tree
Showing 16 changed files with 386 additions and 86 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,11 @@ Mobile apps support the same functionality and modules as the web version of you

<a href="https://freescout.net/android-app/" target="_blank" rel="nofollow"><img alt="Android App" src="https://freescout-helpdesk.github.io/img/apps/android.png" width="200px" /></a> <a href="https://freescout.net/ios-app/" target="_blank" rel="nofollow"><img alt="iOS App" src="https://freescout-helpdesk.github.io/img/apps/ios.png?v=1" width="200px" /></a>

[MacOS Menu Bar App](https://github.com/jonalaniz/scouter)

## Requirements

FreeScout is a pure PHP/MySQL application, so it can be easily deployed even on a [shared hosting](https://2ly.link/20rhd).
FreeScout is a pure PHP/MySQL application, so it can be easily deployed even on a [shared hosting](https://github.com/freescout-help-desk/freescout/wiki/Choosing-a-Server).

* Nginx / Apache / IIS
* PHP 7.1 - 8.x
Expand Down
3 changes: 2 additions & 1 deletion app/Console/Commands/FetchEmails.php
Original file line number Diff line number Diff line change
Expand Up @@ -1618,7 +1618,8 @@ public function createCustomers($emails, $exclude_emails)

public function setSeen($message, $mailbox)
{
$message->setFlag(['Seen']);
$flag = \Eventy::filter('fetch_emails.set_seen_flag', ['Seen'], $message, $mailbox);
$message->setFlag($flag);
\Eventy::action('fetch_emails.after_set_seen', $message, $mailbox, $this);
}
}
13 changes: 11 additions & 2 deletions app/Console/Commands/GenerateVars.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,17 @@ public function handle()

$content = view('js/vars', $params)->render();

//$filesystem->put($file_path, $content);
// Save vars only if content changed
// Escape quotes in json values.
// https://github.com/freescout-help-desk/freescout/issues/4369
$content = preg_replace_callback(
"#(:[ ]*\")(.*)(\"[,\r\n])#",
function($v) {
return $v[1].str_replace('"', '\"', $v[2]).$v[3];
},
$content
);

// Save vars only if content has changed.
try {
if (\Storage::exists('js/vars.js')) {
$old_content = \Storage::get('js/vars.js');
Expand Down
4 changes: 3 additions & 1 deletion app/Http/Controllers/ConversationsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -2563,9 +2563,11 @@ public function ajaxHtmlMoveConv()
abort(403);
}

$mailboxes = \Eventy::filter( 'conversations.move_conv.mailboxes', $user->mailboxesCanView() );

return view('conversations/ajax_html/move_conv', [
'conversation' => $conversation,
'mailboxes' => $user->mailboxesCanView(),
'mailboxes' => $mailboxes,
]);
}

Expand Down
2 changes: 2 additions & 0 deletions app/Listeners/LogFailedLogin.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public function __construct()
*/
public function handle(Failed $event)
{
\Eventy::action('listeners.failed_login', $event);

activity()
//->causedBy($event->user)
->withProperties(['ip' => app('request')->ip(), 'email' => request()->email])
Expand Down
178 changes: 178 additions & 0 deletions app/Misc/ConversationActionButtons.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
<?php

namespace App\Misc;

class ConversationActionButtons
{
// Location constants
const LOCATION_TOOLBAR = 'toolbar';
const LOCATION_DROPDOWN = 'dropdown';
const LOCATION_BOTH = 'both';

/**
* Get all available conversation actions
*/
public static function getActions($conversation, $user, $mailbox)
{
$actions = [
// Default toolbar actions
'reply' => [
'icon' => 'glyphicon-share-alt',
'location' => self::LOCATION_TOOLBAR,
'label' => __('Reply'),
'permission' => function ($conversation) {
return ( ! $conversation->isPhone() || ( $conversation->customer && $conversation->customer->getMainEmail() ) )
&& \Eventy::filter('conversation.reply_button.enabled', true, $conversation);
},
'class' => 'conv-reply',
'fixed_location' => true,
],
'note' => [
'icon' => 'glyphicon-edit',
'location' => self::LOCATION_TOOLBAR,
'label' => __('Note'),
'permission' => function ($conversation) {
return \Eventy::filter('conversation.note_button.enabled', true, $conversation);
},
'class' => 'conv-add-note',
'fixed_location' => true,
],
'delete' => [
'icon' => 'glyphicon-trash',
'location' => self::LOCATION_TOOLBAR,
'label' => $conversation->state != \App\Conversation::STATE_DELETED ? __('Delete') : __('Delete Forever'),
'permission' => function ($conversation, $user) {
return $user->can('delete', $conversation);
},
'class' => $conversation->state != \App\Conversation::STATE_DELETED ? 'conv-delete' : 'conv-delete-forever',
'fixed_location' => true,
],
'delete_mobile' => [
'icon' => 'glyphicon-trash',
'location' => self::LOCATION_DROPDOWN,
'label' => $conversation->state != \App\Conversation::STATE_DELETED ? __('Delete') : __('Delete Forever'),
'permission' => function ($conversation, $user) {
return $user->can('delete', $conversation);
},
'class' => $conversation->state != \App\Conversation::STATE_DELETED ? 'conv-delete' : 'conv-delete-forever',
'fixed_location' => true,
'mobile_only' => true,
],

// Default dropdown actions
'follow' => [
'icon' => 'glyphicon-bell',
'location' => self::LOCATION_DROPDOWN,
'label' => __('Follow'),
'permission' => function () {
return true;
},
'class' => 'conv-follow',
'has_opposite' => true,
'opposite' => [
'label' => __('Unfollow'),
'class' => 'conv-follow',
],
'fixed_location' => true,
],
'forward' => [
'icon' => 'glyphicon-arrow-right',
'location' => self::LOCATION_DROPDOWN,
'label' => __('Forward'),
'permission' => function () {
return true;
},
'class' => 'conv-forward',
'fixed_location' => true,
],
'merge' => [
'icon' => 'glyphicon-indent-left',
'location' => self::LOCATION_DROPDOWN,
'label' => __('Merge'),
'permission' => function ($conversation) {
return ! $conversation->isChat();
},
'class' => '',
'url' => function ($conversation) {
return route('conversations.ajax_html', array_merge([ 'action' => 'merge_conv' ], \Request::all(), [ 'conversation_id' => $conversation->id ]));
},
'attrs' => [
'data-trigger' => 'modal',
'data-modal-title' => __('Merge Conversations'),
'data-modal-no-footer' => 'true',
'data-modal-on-show' => 'initMergeConv',
],
'fixed_location' => true,
],
'move' => [
'icon' => 'glyphicon-log-out',
'location' => self::LOCATION_DROPDOWN,
'label' => __('Move'),
'permission' => function ($conversation, $user) {
return $user->can('move', \App\Conversation::class);
},
'class' => '',
'url' => function ($conversation) {
return route('conversations.ajax_html', array_merge([ 'action' => 'move_conv' ], \Request::all(), [ 'conversation_id' => $conversation->id ]));
},
'attrs' => [
'data-trigger' => 'modal',
'data-modal-title' => __('Move Conversation'),
'data-modal-no-footer' => 'true',
'data-modal-on-show' => 'initMoveConv',
],
'fixed_location' => true,
],
'print' => [
'icon' => 'glyphicon-print',
'location' => self::LOCATION_DROPDOWN,
'label' => __('Print'),
'class' => '',
'permission' => function () {
return true;
},
'url' => function () {
return \Request::getRequestUri() . '&amp;print=1';
},
'attrs' => [
'target' => '_blank',
],
'fixed_location' => true,
],
];

// Allow overriding default actions while preserving backwards compatibility
$actions = \Eventy::filter('conversation.get_action_buttons', $actions, $conversation, $user, $mailbox);

// Filter actions based on permissions
foreach ($actions as $key => $action) {
if (! $action['permission']($conversation, $user)) {
unset($actions[ $key ]);
}
}

return $actions;
}

/**
* Get actions for a specific location
*/
public static function getActionsByLocation($actions, $location)
{
return array_filter($actions, function ($action) use ($location) {
if (! empty($action['fixed_location'])) {
// Modified logic for responsive display
if ($location === self::LOCATION_TOOLBAR) {
// Show in toolbar if it's a toolbar action
return $action['location'] === self::LOCATION_TOOLBAR;
} elseif ($location === self::LOCATION_DROPDOWN) {
// Show in dropdown if explicitly flagged or if it's a dropdown action
return $action['location'] === self::LOCATION_DROPDOWN ||
( ! empty($action['show_in_dropdown']) && $action['location'] === self::LOCATION_TOOLBAR );
}
}

return $action['location'] === $location;
});
}
}
14 changes: 13 additions & 1 deletion app/Misc/Helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -1220,7 +1220,19 @@ public static function queueWorkerRestart()
*/
public static function strSplitKeepWords($str, $max_length = 75)
{
$array_words = explode(' ', $str);
$space = html_entity_decode('&nbsp;');

$str = strtr($str, [
'' => ''.$space,
'' => ''.$space,
// '.' => '.'.$space,
// ',' => ','.$space,
//':' => ':'.$space,
// '—' => '—'.$space,
// '।' => '।'.$space,
]);

$array_words = explode($space, $str);

$currentLength = 0;

Expand Down
4 changes: 2 additions & 2 deletions config/app.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
| or any other location as required by the application or its packages.
*/

'version' => '1.8.159',
'version' => '1.8.160',

/*
|--------------------------------------------------------------------------
Expand Down Expand Up @@ -289,7 +289,7 @@
|
|-------------------------------------------------------------------------
*/
'no_retry_mail_errors' => env('APP_NO_RETRY_MAIL_ERRORS', '(no valid recipients|does not comply with RFC|message file too big)'),
'no_retry_mail_errors' => env('APP_NO_RETRY_MAIL_ERRORS', '(no valid recipients|does not comply with RFC|message file too big|malformed address)'),

/*
|--------------------------------------------------------------------------
Expand Down
2 changes: 1 addition & 1 deletion config/purifier.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
'settings' => [
'default' => [
'HTML.Doctype' => 'HTML 4.01 Transitional',
'HTML.Allowed' => 'div[style],b,strong,i,em,u,a[href|title|style],ul,ol,li,p[style],br,span[style],img[width|height|alt|src|style],table[style|border|bgcolor|cellspacing|cellpadding|border|width],tr[style|bgcolor],td[style|colspan|rowspan|width|bgcolor|border|valign|align],th[style|colspan|rowspan],thead,tfoot,tbody,blockquote,pre,s,strike,font[style|color],h1[style],h2[style],h3[style],h4[style],h5[style],h6,center',
'HTML.Allowed' => 'div[style],b,strong,i,em,u,a[href|title|style],ul,ol,li,p[style],br,span[style],img[width|height|alt|src|style],table[style|border|bgcolor|cellspacing|cellpadding|border|width|class],tr[style|bgcolor],td[style|colspan|rowspan|width|bgcolor|border|valign|align],th[style|colspan|rowspan],thead,tfoot,tbody,blockquote,pre,s,strike,font[style|color],h1[style],h2[style],h3[style],h4[style],h5[style],h6,center',
'CSS.AllowedProperties' => 'display,overflow,border-radius,letter-spacing,white-space,font-size,margin,margin-top,margin-right,margin-bottom,margin-left,background,text-transform,max-width,max-height,width,height,font,padding,padding-top,padding-right,padding-bottom,padding-left,font-family,border-color,font-weight,font-style,text-decoration,color,background-color,text-align,border,border-top,border-left,border-bottom,border-right',
'AutoFormat.AutoParagraph' => true,
'AutoFormat.RemoveEmpty' => true,
Expand Down
16 changes: 14 additions & 2 deletions overrides/webklex/php-imap/src/Header.php
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,20 @@ public function find($pattern) {
* @return string|null
*/
public function getBoundary() {
$regex = $this->config["boundary"] ?? "/boundary=(.*?(?=;)|(.*))/i";
$boundary = $this->find($regex);
// Finding boundary via regex is not 100% reliable as boundary
// may be mentioned in other headers.
$boundary = null;
if (is_object($this->boundary)) {
$values = $this->boundary->get();
if (!empty($values[0])) {
$boundary = $values[0];
}
}

if (!$boundary) {
$regex = $this->config["boundary"] ?? "/boundary=(.*?(?=;)|(.*))/i";
$boundary = $this->find($regex);
}

if ($boundary === null) {
return null;
Expand Down
3 changes: 2 additions & 1 deletion public/css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -3176,7 +3176,8 @@ ul.sidebar-block-list {
top: -2px;
}
.onoffswitch-checkbox {
display: none;
position: absolute;
left:-9999px;
}
.onoffswitch-label {
display: block; overflow: hidden; cursor: pointer;
Expand Down
7 changes: 6 additions & 1 deletion public/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ var EditorInsertVarButton = function (context) {

vars = fsApplyFilter('editor.vars', vars);

var contents = '<select class="form-control summernote-inservar" tabindex="-1">'+
var contents = '<select class="form-control summernote-inservar">'+
'<option value="">'+Lang.get("messages.insert_var")+' ...</option>';
for (var entity_name in vars) {
contents += '<optgroup label="'+Lang.get("messages."+entity_name)+'">';
Expand Down Expand Up @@ -5710,4 +5710,9 @@ function isChatMode()
function reloadPage()
{
window.location.href = '';
}

function getLocale()
{
return $('html:first').attr('lang');
}
Loading

0 comments on commit 178ac6d

Please sign in to comment.