Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 244
0.00% covered (danger)
0.00%
0 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
UserDashboardController
0.00% covered (danger)
0.00%
0 / 244
0.00% covered (danger)
0.00%
0 / 4
4422
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 statistics
0.00% covered (danger)
0.00%
0 / 75
0.00% covered (danger)
0.00%
0 / 1
210
 extension
0.00% covered (danger)
0.00%
0 / 54
0.00% covered (danger)
0.00%
0 / 1
2
 restrictions
0.00% covered (danger)
0.00%
0 / 114
0.00% covered (danger)
0.00%
0 / 1
2550
1<?php
2
3namespace App\Http\Controllers\v1;
4
5use App\Http\Controllers\Controller;
6use App\Http\Models\Auth\User;
7use App\Http\Models\FlyMsgUserDailyUsage;
8use App\Http\Models\HubspotProperties;
9use App\Http\Models\Plans;
10use App\Http\Models\Shortcut;
11use App\Http\Models\Template;
12use App\Http\Models\TemplateCategory;
13use App\Http\Models\UserInfo;
14use App\Http\Services\StatisticsService;
15use App\Traits\SubscriptionTrait;
16use Carbon\Carbon;
17use DOMDocument;
18use Illuminate\Http\JsonResponse;
19use Illuminate\Http\Request;
20use Illuminate\Support\Facades\DB;
21
22class UserDashboardController extends Controller
23{
24    use SubscriptionTrait;
25
26    /**
27     * The statistics service implementation.
28     *
29     * @var StatisticsService
30     */
31    protected $statisticsService;
32
33    /**
34     * Create a new controller instance.
35     *
36     *
37     * @return void
38     */
39    public function __construct(StatisticsService $statisticsService)
40    {
41        $this->statisticsService = $statisticsService;
42    }
43
44    /**
45     * Get the statistics record for the user
46     */
47    public function statistics(Request $request): JsonResponse
48    {
49        $user = $request->user();
50
51        $currentPlan = $this->getCurrentPlan($user);
52        $planIdentifier = $currentPlan->identifier;
53
54        $shortcutCount = Shortcut::count();
55        $searchArray = ['user_defined' => false];
56        $templateAddedCount = Shortcut::where($searchArray)->count();
57
58        $totalAllowedShortcuts = $this->getFeatureByNewKey($currentPlan, 'shortcuts', 0);
59        $totalAllowedCategories = $this->getFeatureByNewKey($currentPlan, 'categories_count', 0);
60        $totalTemplatesAvailable = $this->getFeatureByNewKey($currentPlan, 'templates', 0);
61
62        // Check for general category Flyplates counts in DB
63        $categoryWhere = ['slug' => 'general'];
64        $categoryId = TemplateCategory::where($categoryWhere)->pluck('_id')->first();
65
66        $templatesWhere = ['category_id' => $categoryId];
67        $templateCounts = Template::where($templatesWhere)->count();
68
69        if ($planIdentifier == Plans::FREEMIUM_IDENTIFIER) {
70            $totalTemplatesAvailable = $templateCounts;
71        } elseif (
72            $planIdentifier == Plans::STARTER_MONTHLY_IDENTIFIER ||
73            $planIdentifier == Plans::STARTER_YEARLY_IDENTIFIER
74        ) {
75            $totalTemplatesAvailable = $templateCounts + intval(50);
76        }
77
78        $isRewardable = $user->rewardable ?? false;
79        $rewardsLevel = $user->rewards_level ?? 0;
80
81        if ($isRewardable) {
82            // Level 1+: Unlimited shortcuts
83            if ($rewardsLevel >= 1) {
84                $totalAllowedShortcuts = -1;
85            }
86
87            // Level 2+: Unlimited characters (not used in response but kept for consistency)
88            // Level 3+: Extra category for freemium/starter
89            if ($rewardsLevel >= 3) {
90                if (
91                    $planIdentifier == Plans::FREEMIUM_IDENTIFIER ||
92                    $planIdentifier == Plans::STARTER_MONTHLY_IDENTIFIER ||
93                    $planIdentifier == Plans::STARTER_YEARLY_IDENTIFIER
94                ) {
95                    $totalAllowedCategories = $totalAllowedCategories + 1;
96                }
97            }
98
99            // Level 4+: Unlimited templates
100            if ($rewardsLevel >= 4) {
101                $totalTemplatesAvailable = -1;
102            }
103        }
104
105        if ($request->query('differenceFromNow') == -1) {
106            $first = FlyMsgUserDailyUsage::where('user_id', $user->getKey())->orderBy('created_at')->first();
107            $fromDate = Carbon::parse($first->created_at)->startOfDay();
108            $from = $fromDate->format('M Y');
109        } else {
110            $fromDate = $this->statisticsService->getDateFromNow($request->query('differenceFromNow'), $request->query('dateType'));
111            $from = $this->statisticsService->getDateFromNowInMonthYear($request->query('differenceFromNow'), $request->query('dateType'));
112        }
113
114        $hubspotRecord = HubspotProperties::firstWhere('flymsg_id', $user->getKey());
115
116        $fromOldRecord = ! $user->charts()->exists();
117        $fromNewRecord = $user->charts()->exists();
118
119        $dailyUsage = FlyMsgUserDailyUsage::where('user_id', $user->getKey())
120            ->where('created_at', '>=', new \MongoDB\BSON\UTCDateTime(strtotime($fromDate->startOfDay()->toDateTimeString()) * 1000));
121        $charactersTyped = (clone $dailyUsage)->sum('characters_typed');
122        $timeSaved = (clone $dailyUsage)->sum('time_saved');
123        $costSaved = (clone $dailyUsage)->sum('cost_savings');
124        $flyGrammar = (clone $dailyUsage)->sum('fly_grammar_actions');
125        $flyGrammarActions = (clone $dailyUsage)->sum('fly_grammar_accepted');
126        $autoCorrectActions = (clone $dailyUsage)->sum('fly_grammar_autocorrect');
127        $autoCompleteActions = (clone $dailyUsage)->sum('fly_grammar_autocomplete');
128        $paragraphRewriteCount = (clone $dailyUsage)->sum('paragraph_rewrite_count');
129
130        $flycutsUsed = $user->flycutsUsed($from);
131
132        if ($fromOldRecord) {
133            $chartData = $this->statisticsService->getOldChart($from, $hubspotRecord);
134
135            unset($chartData['totalflycuts_used']);
136        } else {
137            $chartData = $user->getChartData($from);
138        }
139
140        $wagePerHour = $this->statisticsService->getWagePerHour($user, now());
141
142        $result = [
143            'shortcuts' => $shortcutCount,
144            'templates' => $templateAddedCount,
145            'total_templates_available' => $totalTemplatesAvailable,
146            'total_shortcuts_available' => $totalAllowedShortcuts,
147            'characters_typed' => $charactersTyped,
148            'flycuts_used' => $flycutsUsed,
149            'time_saved' => $timeSaved,
150            'cost_saved' => $costSaved,
151            'chart_data' => $chartData,
152            'wage_per_hour' => $wagePerHour,
153            // From New Data
154            'chart_data_New' => $fromNewRecord ? $user->getChartData($from) : [],
155            'created_at' => $user->created_at,
156            'issues_fixed' => $flyGrammarActions,
157            'issues_autocorrect' => $autoCorrectActions,
158            'issues_autocomplete' => $autoCompleteActions,
159            'paragraph_rewrite_count' => $paragraphRewriteCount,
160            'grammar' => $flyGrammar,
161        ];
162
163        return response()->json($result);
164    }
165
166    public function extension(Request $request): JsonResponse
167    {
168        $user = $request->user();
169        $userInfo = UserInfo::where('user_id', $user->id)->first();
170        $plan = $this->getCurrentPlan($user);
171        $today = FlyMsgUserDailyUsage::where('user_id', $user->id)
172            ->where('created_at', '>=', new \MongoDB\BSON\UTCDateTime(strtotime(now()->startOfDay()->toDateTimeString()) * 1000))
173            ->first();
174
175        $thisMonth = FlyMsgUserDailyUsage::where('user_id', $user->id)
176            ->where('created_at', '>=', new \MongoDB\BSON\UTCDateTime(strtotime(now()->startOfMonth()->toDateTimeString()) * 1000));
177
178        $thisYear = FlyMsgUserDailyUsage::where('user_id', $user->id)
179            ->where('created_at', '>=', new \MongoDB\BSON\UTCDateTime(strtotime(now()->startOfYear()->toDateTimeString()) * 1000));
180
181        $result = [
182            'all' => [
183                'flycuts' => $userInfo->total___of_times_flycut_used__count_ ?? 0,
184                'grammar' => $userInfo->total___of_times_flygrammar_is_used_count ?? 0,
185                'engage' => $userInfo->total___of_times_flyengage_used__count_ ?? 0,
186                'post' => $userInfo->total___of_times_flyposts_used__count_ ?? 0,
187                'time_saved' => round($userInfo->total_time_saved_by_flymsg_by_user ?? 0, 2),
188                'cost_saved' => round($userInfo->total_cost_savings_by_flymsg_by_user ?? 0, 2),
189                'characters_typed' => $userInfo->total___of_characters_typed_by_flymsg_by_user ?? 0,
190            ],
191            'today' => [
192                'flycuts' => $today->flycut_count ?? 0,
193                'grammar' => $today->fly_grammar_actions ?? 0,
194                'engage' => $today->flyengage_count ?? 0,
195                'post' => $today->flypost_count ?? 0,
196                'time_saved' => round($today->time_saved ?? 0, 2),
197                'cost_saved' => round($today->cost_savings ?? 0, 2),
198                'characters_typed' => $today->characters_typed ?? 0,
199            ],
200            'this_month' => [
201                'flycuts' => $thisMonth->sum('flycut_count') ?? 0,
202                'grammar' => $thisMonth->sum('fly_grammar_actions') ?? 0,
203                'engage' => $thisMonth->sum('flyengage_count') ?? 0,
204                'post' => $thisMonth->sum('flypost_count') ?? 0,
205                'time_saved' => round($thisMonth->sum('time_saved') ?? 0, 2),
206                'cost_saved' => round($thisMonth->sum('cost_savings') ?? 0, 2),
207                'characters_typed' => $thisMonth->sum('characters_typed') ?? 0,
208            ],
209            'this_year' => [
210                'flycuts' => $thisYear->sum('flycut_count') ?? 0,
211                'grammar' => $thisYear->sum('fly_grammar_actions') ?? 0,
212                'engage' => $thisYear->sum('flyengage_count') ?? 0,
213                'post' => $thisYear->sum('flypost_count') ?? 0,
214                'time_saved' => round($thisYear->sum('time_saved') ?? 0, 2),
215                'cost_saved' => round($thisYear->sum('cost_savings') ?? 0, 2),
216                'characters_typed' => $thisYear->sum('characters_typed') ?? 0,
217            ],
218            'quota' => [
219                'flycuts' => $plan->flycut_deployment ?? -1,
220                'grammar' => $plan->flygrammar_actions ?? -1,
221                'ai' => $plan->prompts_per_day ?? -1,
222            ],
223        ];
224
225        return response()->json($result);
226    }
227
228    public function restrictions()
229    {
230        $userRestrictions = [];
231        $users = DB::table('users')->get();
232
233        foreach ($users as $user) {
234            $userId = (string) $user->_id;
235
236            $userModel = User::where(['_id' => $userId])->first();
237            $currentSubscription = $userModel->subscription('main');
238
239            if ($currentSubscription) {
240                $plan = $currentSubscription->plan;
241
242                $userRestrictions[$userId]['is_valid'] = true;
243                $userRestrictions[$userId]['email'] = $userModel->email;
244
245                $shortcutSql = DB::table('shortcuts')
246                    ->where('user_id', '=', $userId);
247
248                $shortcuts = $shortcutSql->get();
249                if (! empty($shortcuts)) {
250                    foreach ($shortcuts as $shortcutHtmlArray) {
251                        $shortcutId = (string) $shortcutHtmlArray->_id;
252                        $userRestrictions[$userId][$shortcutId]['is_valid'] = true;
253
254                        $shortcuttext = $shortcutHtmlArray->text;
255                        $shortcuttext = str_replace("\n", '', str_replace(PHP_EOL, '', $shortcuttext));
256
257                        /** Check for Bold Text */
258                        $bold = $this->getFeatureByNewKey($plan, 'bold', false);
259                        $hasBoldTextInHtml = $this->hasTag('/<strong>(.+?)<\/strong>/m', $shortcuttext);
260
261                        if (! $bold && $hasBoldTextInHtml) {
262                            $userRestrictions[$userId]['is_valid'] = false;
263                        }
264
265                        /** Check for Italic Text */
266                        $italic = $this->getFeatureByNewKey($plan, 'italic', false);
267                        $hasItalicTextInHtml = $this->hasTag('/<em>(.+?)<\/em>/m', $shortcuttext);
268
269                        if (! $italic && $hasItalicTextInHtml) {
270                            $userRestrictions[$userId]['is_valid'] = false;
271                        }
272
273                        $shortcutHtml = $shortcutHtmlArray->html;
274                        $shortcutHtmlStyles = $this->extractStylesFromHtml($shortcutHtml);
275
276                        /** Check for underline */
277                        $underLine = $this->getFeatureByNewKey($plan, 'underline', false);
278                        $underLineExists = $this->checkIfStyleExists('text-decoration', 'underline', $shortcutHtmlStyles);
279
280                        if ($underLineExists && ! $underLine) {
281                            $userRestrictions[$userId]['is_valid'] = false;
282                        }
283
284                        /** Strikethrough */
285                        $strikeThrough = $this->getFeatureByNewKey($plan, 'strikethrough', false);
286                        $strikeThroughExists = $this->checkIfStyleExists('text-decoration', 'line-through', $shortcutHtmlStyles);
287
288                        if ($strikeThroughExists && ! $strikeThrough) {
289                            $userRestrictions[$userId]['is_valid'] = false;
290                        }
291
292                        /** Hyperlink */
293                        $hyperlink = $this->getFeatureByNewKey($plan, 'hyperlink', false);
294                        $hasHyperlinkHtml = $this->hasTag('/<a(.+?)<\/a>/m', $shortcutHtml);
295
296                        if ($hasHyperlinkHtml && ! $hyperlink) {
297                            $userRestrictions[$userId]['is_valid'] = false;
298                        }
299
300                        /** Alignment - Left */
301                        $alignmentLeft = $this->getFeatureByNewKey($plan, 'alignment', false);
302                        $alignmentLeftExists = $this->checkIfStyleExists('text-align', 'left', $shortcutHtmlStyles);
303
304                        if ($alignmentLeftExists && ! in_array('left', $alignmentLeft)) {
305                            $userRestrictions[$userId]['is_valid'] = false;
306                        }
307
308                        /** Alignment - Centered */
309                        $alignmentCenter = $this->getFeatureByNewKey($plan, 'alignment', false);
310                        $alignmentCenterExists = $this->checkIfStyleExists('text-align', 'center', $shortcutHtmlStyles);
311
312                        if ($alignmentCenterExists && ! in_array('center', $alignmentCenter)) {
313                            $userRestrictions[$userId]['is_valid'] = false;
314                        }
315
316                        /** Alignment - Right */
317                        $alignmentRight = $this->getFeatureByNewKey($plan, 'alignment', false);
318                        $alignmentRightExists = $this->checkIfStyleExists('text-align', 'right', $shortcutHtmlStyles);
319
320                        if ($alignmentRightExists && ! in_array('right', $alignmentRight)) {
321                            $userRestrictions[$userId]['is_valid'] = false;
322                        }
323
324                        /** Alignment - Justify */
325                        $alignmentJustify = $this->getFeatureByNewKey($plan, 'alignment', false);
326                        $alignmentJustifyExists = $this->checkIfStyleExists('text-align', 'justify', $shortcutHtmlStyles);
327
328                        if ($alignmentJustifyExists && ! in_array('justify', $alignmentJustify)) {
329                            $userRestrictions[$userId]['is_valid'] = false;
330                        }
331
332                        /** Font Size */
333                        $fontSize = $this->getFeatureByNewKey($plan, 'font_size', false);
334                        $fontSizeMatch = $this->checkIfStyleExists('font-size', $fontSize.'px', $shortcutHtmlStyles);
335
336                        if ($fontSize != -1 && ! $fontSizeMatch && array_key_exists('font-size', $shortcutHtmlStyles)) {
337                            $userRestrictions[$userId]['is_valid'] = false;
338                        }
339
340                        /** Font Family */
341                        $font = $this->getFeatureByNewKey($plan, 'font_family', false);
342                        if ($font == 'Arial') {
343                            $fontFamilyMatch1 = $this->checkIfStyleExists('font-family', 'arial, helvetica, sans-serif', $shortcutHtmlStyles);
344                            $fontFamilyMatch2 = $this->checkIfStyleExists('font-family', 'Arial', $shortcutHtmlStyles);
345
346                            if (! $fontFamilyMatch1 && ! $fontFamilyMatch2 && array_key_exists('font-family', $shortcutHtmlStyles)) {
347                                $userRestrictions[$userId]['is_valid'] = false;
348                            }
349                        } else {
350                            if ($font != -1 && $font) {
351                                $countUniqueFonts = $this->countStyles('font-family', $shortcutHtmlStyles);
352
353                                if ($countUniqueFonts > $font) {
354                                    $userRestrictions[$userId]['is_valid'] = false;
355                                }
356                            }
357                        }
358
359                        /** Bullet Points */
360                        $bulletPoints = $this->getFeatureByNewKey($plan, 'bullet_points', false);
361                        $hasBulletPointsHtml = $this->hasTag('/<ul(.+?)<\/ul>/m', $shortcutHtml);
362
363                        if ($hasBulletPointsHtml && ! $bulletPoints) {
364                            $userRestrictions[$userId]['is_valid'] = false;
365                        }
366
367                        /** Numbered List */
368                        $numberedList = $this->getFeatureByNewKey($plan, 'numbered_list', false);
369                        $hasNumberedList = $this->hasTag('/<ol(.+?)<\/ol>/m', $shortcutHtml);
370
371                        if ($hasNumberedList && ! $numberedList) {
372                            $userRestrictions[$userId]['is_valid'] = false;
373                        }
374
375                        /** Increase Indent */
376                        $increaseIndent = $this->getFeatureByNewKey($plan, 'indent', false);
377                        $hasIncreaseIndent = array_key_exists('padding-left', $shortcutHtmlStyles);
378
379                        if ($hasIncreaseIndent && ! $increaseIndent) {
380                            $userRestrictions[$userId]['is_valid'] = false;
381                        }
382
383                        /** Giphy checkIfGiphyExists */
384                        $allowGiphy = $this->getFeatureByNewKey($plan, 'giphys', false);
385                        $doc = new DOMDocument;
386                        @$doc->loadHTML($shortcutHtml);
387                        $imageTags = $doc->getElementsByTagName('img');
388
389                        $gifs = collect($imageTags)->filter(function ($tag) {
390                            return (pathinfo(parse_url($tag->getAttribute('src'), PHP_URL_PATH), PATHINFO_EXTENSION) == 'gif')
391                                && ($this->getDomainName($tag->getAttribute('src')) == 'media2.giphy.com');
392                        });
393
394                        if (! $allowGiphy && $gifs->isNotEmpty()) {
395                            $userRestrictions[$userId]['is_valid'] = false;
396                        }
397
398                        /** flycut character count checkCharacterCount */
399                        $flycutCharLimit = $this->getFeatureByNewKey($plan, 'character_limit', 0);
400                        $charCount = strlen($shortcutHtml);
401
402                        if ($charCount > $flycutCharLimit && $flycutCharLimit > -1) {
403                            $userRestrictions[$userId]['is_valid'] = false;
404                        }
405                    }
406
407                    /** flycut counts checkFlyCutsCount */
408                    $numberOfFlyCutsThatCanBeCreated = $this->getFeatureByNewKey($plan, 'shortcuts', 0);
409                    $currentFlycutsCreated = $shortcutSql->count();
410
411                    if ($currentFlycutsCreated >= $numberOfFlyCutsThatCanBeCreated && $numberOfFlyCutsThatCanBeCreated > -1) {
412                        $userRestrictions[$userId]['is_valid'] = false;
413                    }
414
415                    /** categories counts checkCategoriesCount */
416                    $categoriesLimit = $this->getFeatureByNewKey($plan, 'categories_count', 0);
417
418                    $categorySql = DB::table('shortcut_categories')
419                        ->where('user_id', '=', $userId);
420                    $categoriesCount = $categorySql->count();
421
422                    if ($categoriesCount >= $categoriesLimit && $categoriesLimit != -1) {
423                        $userRestrictions[$userId]['is_valid'] = false;
424                    }
425
426                    /** sub categories counts checkSubCategoriesCount */
427                    $subCategoriesLimit = $this->getFeatureByNewKey($plan, 'subcategories_count', 0);
428                    $subcategorySql = DB::table('shortcut_sub_categories_lv1')
429                        ->where('user_id', '=', $userId);
430                    $subCategoriesCount = $subcategorySql->count();
431
432                    if ($subCategoriesCount > $subCategoriesLimit && $subCategoriesLimit != -1) {
433                        $userRestrictions[$userId]['is_valid'] = false;
434                    }
435                }
436            }
437        }
438
439        return response()->json($userRestrictions);
440    }
441}