Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • issue/project_browser-3278972
  • issue/project_browser-3240319
  • project/project_browser
  • issue/project_browser-3223747
  • issue/project_browser-3238996
  • issue/project_browser-3279510
  • issue/project_browser-3281209
  • issue/project_browser-3282592
  • issue/project_browser-3282691
  • issue/project_browser-3282681
  • issue/project_browser-3283861
  • issue/project_browser-3279918
  • issue/project_browser-3280627
  • issue/project_browser-3281216
  • issue/project_browser-3281692
  • issue/project_browser-3281891
  • issue/project_browser-3282201
  • issue/project_browser-3282707
  • issue/project_browser-3282732
  • issue/project_browser-3279927
  • issue/project_browser-3280190
  • issue/project_browser-3268626
  • issue/project_browser-3280706
  • issue/project_browser-3281388
  • issue/project_browser-3282777
  • issue/project_browser-3278333
  • issue/project_browser-3280167
  • issue/project_browser-3281252
  • issue/project_browser-3280006
  • issue/project_browser-3281686
  • issue/project_browser-3282698
  • issue/project_browser-3282688
  • issue/project_browser-3283826
  • issue/project_browser-3279676
  • issue/project_browser-3280176
  • issue/project_browser-3281327
  • issue/project_browser-3281624
  • issue/project_browser-3281217
  • issue/project_browser-3281187
  • issue/project_browser-3281200
  • issue/project_browser-3282591
  • issue/project_browser-3282648
  • issue/project_browser-3282705
  • issue/project_browser-3282589
  • issue/project_browser-3282692
  • issue/project_browser-3283683
  • issue/project_browser-3283825
  • issue/project_browser-3283875
  • issue/project_browser-3282709
  • issue/project_browser-3281418
  • issue/project_browser-3281620
  • issue/project_browser-3282499
  • issue/project_browser-3282778
  • issue/project_browser-3282683
  • issue/project_browser-3282700
  • issue/project_browser-3240318
  • issue/project_browser-3240128
  • issue/project_browser-3245770
  • issue/project_browser-3248299
  • issue/project_browser-3240321
  • issue/project_browser-3249483
  • issue/project_browser-3241358
  • issue/project_browser-3238572
  • issue/project_browser-3249958
  • issue/project_browser-3240320
  • issue/project_browser-3250132
  • issue/project_browser-3239477
  • issue/project_browser-3250356
  • issue/project_browser-3250413
  • issue/project_browser-3250454
  • issue/project_browser-3277551
  • issue/project_browser-3258248
  • issue/project_browser-3258247
  • issue/project_browser-3259324
  • issue/project_browser-3259657
  • issue/project_browser-3245948
  • issue/project_browser-3264474
  • issue/project_browser-3267656
  • issue/project_browser-3267658
  • issue/project_browser-3267661
  • issue/project_browser-3268636
  • issue/project_browser-3224709
  • issue/project_browser-3273634
  • issue/project_browser-3274010
  • issue/project_browser-3274277
  • issue/project_browser-3252674
  • issue/project_browser-3274701
  • issue/project_browser-3274520
  • issue/project_browser-3274906
  • issue/project_browser-3275413
  • issue/project_browser-3275536
  • issue/project_browser-3243019
  • issue/project_browser-3274577
  • issue/project_browser-3274335
  • issue/project_browser-3270210
  • issue/project_browser-3277052
  • issue/project_browser-3224896
  • issue/project_browser-3274897
  • issue/project_browser-3277253
  • issue/project_browser-3277260
  • issue/project_browser-3277264
  • issue/project_browser-3277764
  • issue/project_browser-3277808
  • issue/project_browser-3277811
  • issue/project_browser-3277832
  • issue/project_browser-3277224
  • issue/project_browser-3278352
  • issue/project_browser-3277255
  • issue/project_browser-3279663
  • issue/project_browser-3279983
  • issue/project_browser-3280721
  • issue/project_browser-3280765
  • issue/project_browser-3283771
  • issue/project_browser-3284094
  • issue/project_browser-3284233
  • issue/project_browser-3284092
  • issue/project_browser-3284312
  • issue/project_browser-3284321
  • issue/project_browser-3284315
  • issue/project_browser-3284333
  • issue/project_browser-3284325
  • issue/project_browser-3284327
  • issue/project_browser-3284463
  • issue/project_browser-3284330
  • issue/project_browser-3284347
  • issue/project_browser-3284332
  • issue/project_browser-3284631
  • issue/project_browser-3284945
  • issue/project_browser-3284880
  • issue/project_browser-3284341
  • issue/project_browser-3284309
  • issue/project_browser-3281218
  • issue/project_browser-3285889
  • issue/project_browser-3285874
  • issue/project_browser-3285880
  • issue/project_browser-3285878
  • issue/project_browser-3285870
  • issue/project_browser-3285443
  • issue/project_browser-3285879
  • issue/project_browser-3269587
  • issue/project_browser-3291995
  • issue/project_browser-3292019
  • issue/project_browser-3292395
  • issue/project_browser-3292562
  • issue/project_browser-3289165
  • issue/project_browser-3292996
  • issue/project_browser-3293014
  • issue/project_browser-3252678
  • issue/project_browser-3277214
  • issue/project_browser-3293691
  • issue/project_browser-3293694
  • issue/project_browser-3267678
  • issue/project_browser-3267685
  • issue/project_browser-3293909
  • issue/project_browser-3293937
  • issue/project_browser-3293898
  • issue/project_browser-3293905
  • issue/project_browser-3282163
  • issue/project_browser-3293899
  • issue/project_browser-3294763
  • issue/project_browser-3294754
  • issue/project_browser-3247332
  • issue/project_browser-3295320
  • issue/project_browser-3294608
  • issue/project_browser-3287419
  • issue/project_browser-3294576
  • issue/project_browser-3293424
  • issue/project_browser-3296264
  • issue/project_browser-3296324
  • issue/project_browser-3296494
  • issue/project_browser-3296250
  • issue/project_browser-3304245
  • issue/project_browser-3304569
  • issue/project_browser-3298525
  • issue/project_browser-3298575
  • issue/project_browser-3293929
  • issue/project_browser-3298755
  • issue/project_browser-3249550
  • issue/project_browser-3249553
  • issue/project_browser-3282842
  • issue/project_browser-3300093
  • issue/project_browser-3299285
  • issue/project_browser-3300447
  • issue/project_browser-3300262
  • issue/project_browser-3303920
  • issue/project_browser-3305515
  • issue/project_browser-3300735
  • issue/project_browser-3299029
  • issue/project_browser-3300520
  • issue/project_browser-3301498
  • issue/project_browser-3301577
  • issue/project_browser-3300397
  • issue/project_browser-3301989
  • issue/project_browser-3302094
  • issue/project_browser-3241731
  • issue/project_browser-3305551
  • issue/project_browser-3303328
  • issue/project_browser-3305983
  • issue/project_browser-3306162
  • issue/project_browser-3310703
  • issue/project_browser-3310725
  • issue/project_browser-3310876
  • issue/project_browser-3310884
  • issue/project_browser-3306717
  • issue/project_browser-3307512
  • issue/project_browser-3307674
  • issue/project_browser-3307917
  • issue/project_browser-3306714
  • issue/project_browser-3308068
  • issue/project_browser-3303496
  • issue/project_browser-3306722
  • issue/project_browser-3308522
  • issue/project_browser-3308525
  • issue/project_browser-3310864
  • issue/project_browser-3310877
  • issue/project_browser-3310898
  • issue/project_browser-3311370
  • issue/project_browser-3293977
  • issue/project_browser-3310893
  • issue/project_browser-3310874
  • issue/project_browser-3271591
  • issue/project_browser-3300090
  • issue/project_browser-3311763
  • issue/project_browser-3304183
  • issue/project_browser-3303522
  • issue/project_browser-3311980
  • issue/project_browser-3311992
  • issue/project_browser-3311978
  • issue/project_browser-3303520
  • issue/project_browser-3312289
  • issue/project_browser-3312537
  • issue/project_browser-3310882
  • issue/project_browser-3282338
  • issue/project_browser-3314210
  • issue/project_browser-3310896
  • issue/project_browser-3315866
  • issue/project_browser-3315853
  • issue/project_browser-3314520
  • issue/project_browser-3303373
  • issue/project_browser-3314728
  • issue/project_browser-3313292
  • issue/project_browser-3300350
  • issue/project_browser-3315867
  • issue/project_browser-3315860
  • issue/project_browser-3315854
  • issue/project_browser-3315858
  • issue/project_browser-3315859
  • issue/project_browser-3310906
  • issue/project_browser-3310903
  • issue/project_browser-3316454
  • issue/project_browser-3316740
  • issue/project_browser-3316744
  • issue/project_browser-3316820
  • issue/project_browser-3316852
  • issue/project_browser-3318442
  • issue/project_browser-3318732
  • issue/project_browser-3318789
  • issue/project_browser-3318905
  • issue/project_browser-3327323
  • issue/project_browser-3325409
  • issue/project_browser-3326049
  • issue/project_browser-3318794
  • issue/project_browser-3316357
  • issue/project_browser-3320295
  • issue/project_browser-3319785
  • issue/project_browser-3316998
  • issue/project_browser-3326245
  • issue/project_browser-3327400
  • issue/project_browser-3327684
  • issue/project_browser-3328136
  • issue/project_browser-3323354
  • issue/project_browser-3325630
  • issue/project_browser-3325658
  • issue/project_browser-3316162
  • issue/project_browser-3318750
  • issue/project_browser-3323531
  • issue/project_browser-3324905
  • issue/project_browser-3322594
  • issue/project_browser-3321703
  • issue/project_browser-3318791
  • issue/project_browser-3277506
  • issue/project_browser-3322164
  • issue/project_browser-3321698
  • issue/project_browser-3317264
  • issue/project_browser-3325657
  • issue/project_browser-3315862
  • issue/project_browser-3329644
  • issue/project_browser-3328124
  • issue/project_browser-3329861
  • issue/project_browser-3330071
  • issue/project_browser-3330102
  • issue/project_browser-3329889
  • issue/project_browser-3330128
  • issue/project_browser-3330129
  • issue/project_browser-3330122
  • issue/project_browser-3330124
  • issue/project_browser-3330887
  • issue/project_browser-3330123
  • issue/project_browser-3330116
  • issue/project_browser-3330130
  • issue/project_browser-3330133
  • issue/project_browser-3332968
  • issue/project_browser-3333456
  • issue/project_browser-3334585
  • issue/project_browser-3304184
  • issue/project_browser-3338357
  • issue/project_browser-3342535
  • issue/project_browser-3340133
  • issue/project_browser-3338356
  • issue/project_browser-3330235
  • issue/project_browser-3341230
  • issue/project_browser-3342128
  • issue/project_browser-3342218
  • issue/project_browser-3345607
  • issue/project_browser-3348249
  • issue/project_browser-3351987
  • issue/project_browser-3348781
  • issue/project_browser-3348872
  • issue/project_browser-3351137
  • issue/project_browser-3353355
  • issue/project_browser-3353483
  • issue/project_browser-3354450
  • issue/project_browser-3356614
  • issue/project_browser-3356685
  • issue/project_browser-3303190
  • issue/project_browser-3359678
  • issue/project_browser-3361123
  • issue/project_browser-3311503
  • issue/project_browser-3364776
  • issue/project_browser-3365345
  • issue/project_browser-3365350
  • issue/project_browser-3318726
  • issue/project_browser-3309273
  • issue/project_browser-3303381
  • issue/project_browser-3360062
  • issue/project_browser-3365404
  • issue/project_browser-3359980
  • issue/project_browser-3323860
  • issue/project_browser-3365180
  • issue/project_browser-3249549
  • issue/project_browser-3365678
  • issue/project_browser-3370284
  • issue/project_browser-3404483
  • issue/project_browser-3365435
  • issue/project_browser-3402908
  • issue/project_browser-3372796
  • issue/project_browser-3357036
  • issue/project_browser-3375619
  • issue/project_browser-3376206
  • issue/project_browser-3376201
  • issue/project_browser-3376202
  • issue/project_browser-3376203
  • issue/project_browser-3376208
  • issue/project_browser-3357003
  • issue/project_browser-3376291
  • issue/project_browser-3382457
  • issue/project_browser-3348761
  • issue/project_browser-3376160
  • issue/project_browser-3348960
  • issue/project_browser-3389246
  • issue/project_browser-3389250
  • issue/project_browser-3310908
  • issue/project_browser-3394166
  • issue/project_browser-3394570
  • issue/project_browser-3394904
  • issue/project_browser-3395472
  • issue/project_browser-3395480
  • issue/project_browser-3360033
  • issue/project_browser-3395478
  • issue/project_browser-3360063
  • issue/project_browser-3365392
  • issue/project_browser-3396245
  • issue/project_browser-3437724
  • issue/project_browser-3343805
  • issue/project_browser-3409134
  • issue/project_browser-3436249
  • issue/project_browser-3444662
  • issue/project_browser-3410505
  • issue/project_browser-3410797
  • issue/project_browser-3410947
  • issue/project_browser-3419177
  • issue/project_browser-3413564
  • issue/project_browser-3413763
  • issue/project_browser-3415079
  • issue/project_browser-3413567
  • issue/project_browser-3312354
  • issue/project_browser-3418454
  • issue/project_browser-3419925
  • issue/project_browser-3413498
  • issue/project_browser-3420501
  • issue/project_browser-3420552
  • issue/project_browser-3421211
  • issue/project_browser-3421699
  • issue/project_browser-3423695
  • issue/project_browser-3423697
  • issue/project_browser-3423915
  • issue/project_browser-3423429
  • issue/project_browser-3423442
  • issue/project_browser-3425989
  • issue/project_browser-3426658
  • issue/project_browser-3365103
  • issue/project_browser-3318817
  • issue/project_browser-3426767
  • issue/project_browser-3443929
  • issue/project_browser-3443930
  • issue/project_browser-3444658
  • issue/project_browser-3446591
  • issue/project_browser-3445765
  • issue/project_browser-3426094
  • issue/project_browser-3443943
  • issue/project_browser-3446092
  • issue/project_browser-3348954
  • issue/project_browser-3446604
  • issue/project_browser-3446414
  • issue/project_browser-3312056
  • issue/project_browser-3446109
  • issue/project_browser-3446257
  • issue/project_browser-3446416
  • issue/project_browser-3445811
  • issue/project_browser-3437721
  • issue/project_browser-3436529
  • issue/project_browser-3446322
  • issue/project_browser-3446528
  • issue/project_browser-3441425
  • issue/project_browser-3427491
  • issue/project_browser-3447377
  • issue/project_browser-3447420
  • issue/project_browser-3447764
  • issue/project_browser-3448808
  • issue/project_browser-3450499
  • issue/project_browser-3451142
  • issue/project_browser-3451659
  • issue/project_browser-3446161
  • issue/project_browser-3451863
  • issue/project_browser-3452431
  • issue/project_browser-3452504
  • issue/project_browser-3452541
  • issue/project_browser-3452737
  • issue/project_browser-3452787
  • issue/project_browser-3452784
  • issue/project_browser-3453039
  • issue/project_browser-3453101
  • issue/project_browser-3453437
  • issue/project_browser-3453766
  • issue/project_browser-3453775
  • issue/project_browser-3453794
  • issue/project_browser-3453808
  • issue/project_browser-3453997
  • issue/project_browser-3454248
  • issue/project_browser-3453738
  • issue/project_browser-3454294
  • issue/project_browser-3455152
  • issue/project_browser-3455284
  • issue/project_browser-3455215
  • issue/project_browser-3455220
  • issue/project_browser-3455477
  • issue/project_browser-3455485
  • issue/project_browser-3455210
  • issue/project_browser-3455715
  • issue/project_browser-3455917
  • issue/project_browser-3365434
  • issue/project_browser-3457003
  • issue/project_browser-3457054
  • issue/project_browser-3457376
  • issue/project_browser-3457682
  • issue/project_browser-3458287
  • issue/project_browser-3371084
  • issue/project_browser-3458765
  • issue/project_browser-3457972
  • issue/project_browser-3452435
  • issue/project_browser-3459662
  • issue/project_browser-3460033
  • issue/project_browser-3454873
  • issue/project_browser-3459288
  • issue/project_browser-3460938
  • issue/project_browser-3461670
  • issue/project_browser-3461518
  • issue/project_browser-3461892
  • issue/project_browser-3461879
  • issue/project_browser-3462173
  • issue/project_browser-3461340
  • issue/project_browser-3463833
  • issue/project_browser-3464077
  • issue/project_browser-3464450
  • issue/project_browser-3461037
  • issue/project_browser-3464794
  • issue/project_browser-3465046
  • issue/project_browser-3450629
  • issue/project_browser-3293907
  • issue/project_browser-3466307
  • issue/project_browser-3458908
  • issue/project_browser-3465232
  • issue/project_browser-3467911
  • issue/project_browser-3458844
  • issue/project_browser-3456978
  • issue/project_browser-3471232
  • issue/project_browser-3472206
  • issue/project_browser-3472230
  • issue/project_browser-3469490
  • issue/project_browser-3472231
  • issue/project_browser-3470540
  • issue/project_browser-3475056
  • issue/project_browser-3475852
  • issue/project_browser-3476400
  • issue/project_browser-3472334
  • issue/project_browser-3476717
  • issue/project_browser-3476812
  • issue/project_browser-3476887
  • issue/project_browser-3477288
  • issue/project_browser-3476818
  • issue/project_browser-3477314
  • issue/project_browser-3477343
  • issue/project_browser-3477232
  • issue/project_browser-3477231
  • issue/project_browser-3478295
  • issue/project_browser-3478832
  • issue/project_browser-3477335
  • issue/project_browser-3478994
  • issue/project_browser-3478437
  • issue/project_browser-3479219
  • issue/project_browser-3477238
  • issue/project_browser-3480064
  • issue/project_browser-3469552
  • issue/project_browser-3479907
  • issue/project_browser-3481084
  • issue/project_browser-3458840
  • issue/project_browser-3483725
  • issue/project_browser-3495318
  • issue/project_browser-3495328
  • issue/project_browser-3496059
  • issue/project_browser-3495317
  • issue/project_browser-3485108
  • issue/project_browser-3476807
  • issue/project_browser-3485386
  • issue/project_browser-3486091
  • issue/project_browser-3485747
  • issue/project_browser-3487842
  • issue/project_browser-3487834
  • issue/project_browser-3487868
  • issue/project_browser-3426179
  • issue/project_browser-3457378
  • issue/project_browser-3462307
  • issue/project_browser-3487835
  • issue/project_browser-3488078
  • issue/project_browser-3488141
  • issue/project_browser-3479924
  • issue/project_browser-3487960
  • issue/project_browser-3486291
  • issue/project_browser-3469860
  • issue/project_browser-3480098
  • issue/project_browser-3489028
  • issue/project_browser-3446603
  • issue/project_browser-3489573
  • issue/project_browser-3489665
  • issue/project_browser-3481652
  • issue/project_browser-3489810
  • issue/project_browser-3489827
  • issue/project_browser-3477950
  • issue/project_browser-3490176
  • issue/project_browser-3489972
  • issue/project_browser-3489126
  • issue/project_browser-3484474
  • issue/project_browser-3491232
  • issue/project_browser-3489422
  • issue/project_browser-3492153
  • issue/project_browser-3483412
  • issue/project_browser-3492108
  • issue/project_browser-3492209
  • issue/project_browser-3488156
  • issue/project_browser-3493332
  • issue/project_browser-3489729
  • issue/project_browser-3493778
  • issue/project_browser-3489054
  • issue/project_browser-3494672
  • issue/project_browser-3494858
  • issue/project_browser-3457966
  • issue/project_browser-3494506
  • issue/project_browser-3494655
  • issue/project_browser-3494978
  • issue/project_browser-3495112
  • issue/project_browser-3494818
  • issue/project_browser-3495168
  • issue/project_browser-3495211
  • issue/project_browser-3495330
  • issue/project_browser-3494819
  • issue/project_browser-3492262
  • issue/project_browser-3497778
  • issue/project_browser-3496236
  • issue/project_browser-3498040
  • issue/project_browser-3498231
  • issue/project_browser-3494512
  • issue/project_browser-3498267
  • issue/project_browser-3493916
  • issue/project_browser-3498560
  • issue/project_browser-3498835
  • issue/project_browser-3498901
  • issue/project_browser-3499390
  • issue/project_browser-3499406
  • issue/project_browser-3500024
  • issue/project_browser-3501036
  • issue/project_browser-3501194
  • issue/project_browser-3498564
  • issue/project_browser-3501590
  • issue/project_browser-3498562
  • issue/project_browser-3499411
  • issue/project_browser-3501766
  • issue/project_browser-3501455
  • issue/project_browser-3501453
  • issue/project_browser-3502161
  • issue/project_browser-3498570
  • issue/project_browser-3502465
  • issue/project_browser-3502399
  • issue/project_browser-3502612
  • issue/project_browser-3502645
  • issue/project_browser-3502666
  • issue/project_browser-3502643
  • issue/project_browser-3502604
  • issue/project_browser-3502850
  • issue/project_browser-3502918
  • issue/project_browser-3487845
  • issue/project_browser-3502935
  • issue/project_browser-3502986
  • issue/project_browser-3503148
  • issue/project_browser-3503140
  • issue/project_browser-3503197
  • issue/project_browser-3502854
  • issue/project_browser-3503137
  • issue/project_browser-3502603
  • issue/project_browser-3497909
  • issue/project_browser-3504656
  • issue/project_browser-3504781
  • issue/project_browser-3504960
  • issue/project_browser-3504997
  • issue/project_browser-3504665
  • issue/project_browser-3505152
  • issue/project_browser-3494824
  • issue/project_browser-3502734
  • issue/project_browser-3505188
  • issue/project_browser-3505249
  • issue/project_browser-3505285
  • issue/project_browser-3505450
  • issue/project_browser-3505607
  • issue/project_browser-3505608
  • issue/project_browser-3505592
  • issue/project_browser-3505700
  • issue/project_browser-3322601
  • issue/project_browser-3505837
  • issue/project_browser-3506252
  • issue/project_browser-3506178
  • issue/project_browser-3453724
  • issue/project_browser-3506517
  • issue/project_browser-3506511
  • issue/project_browser-3505159
  • issue/project_browser-3506547
  • issue/project_browser-3506557
  • issue/project_browser-3506377
  • issue/project_browser-3506454
  • issue/project_browser-3504655
  • issue/project_browser-3507468
  • issue/project_browser-3507536
  • issue/project_browser-3507922
  • issue/project_browser-3508090
  • issue/project_browser-3508182
  • issue/project_browser-3508334
  • issue/project_browser-3508547
  • issue/project_browser-3508350
  • issue/project_browser-3508631
  • issue/project_browser-3508786
  • issue/project_browser-3508956
  • issue/project_browser-3509170
  • issue/project_browser-3509222
  • issue/project_browser-3509184
  • issue/project_browser-3509242
  • issue/project_browser-3506513
  • issue/project_browser-3508906
  • issue/project_browser-3508971
  • issue/project_browser-3508969
  • issue/project_browser-3509375
  • issue/project_browser-3509380
  • issue/project_browser-3509449
  • issue/project_browser-3509499
  • issue/project_browser-3510420
  • issue/project_browser-3509730
  • issue/project_browser-3509217
  • issue/project_browser-3509733
  • issue/project_browser-3510642
  • issue/project_browser-3510506
  • issue/project_browser-3510796
  • issue/project_browser-3511079
  • issue/project_browser-3509162
  • issue/project_browser-3509674
  • issue/project_browser-3511104
  • issue/project_browser-3511069
  • issue/project_browser-3511080
  • issue/project_browser-3506565
  • issue/project_browser-3511246
  • issue/project_browser-3511417
  • issue/project_browser-3511994
  • issue/project_browser-3511927
  • issue/project_browser-3511942
  • issue/project_browser-3512609
  • issue/project_browser-3512610
  • issue/project_browser-3512781
  • issue/project_browser-3512785
  • issue/project_browser-3512856
  • issue/project_browser-3509738
  • issue/project_browser-3504664
  • issue/project_browser-3515746
  • issue/project_browser-3451012
  • issue/project_browser-3512831
  • issue/project_browser-3513763
  • issue/project_browser-3500926
  • issue/project_browser-3394832
713 results
Show changes
Commits on Source (198)
Showing
with 1537 additions and 434 deletions
......@@ -10,3 +10,7 @@ tabwise
sophron
BOLDRED
NOCHANGES
CURLOPT
RETURNTRANSFER
varchar
techdebt
......@@ -4,3 +4,4 @@ composer.lock
.DS_Store
node_modules
sveltejs/report*
sveltejs/.yarn/install-state.gz
......@@ -39,6 +39,13 @@ include:
- '/includes/include.drupalci.variables.yml'
- '/includes/include.drupalci.workflows.yml'
stages:
- development
- build
- validate
- test
- publish
################
# Pipeline configuration variables
#
......@@ -49,6 +56,7 @@ include:
variables:
# For parallel running, needs more CPU resources.
KUBERNETES_CPU_REQUEST: 16
OPT_IN_TEST_PREVIOUS_MAJOR: '1'
OPT_IN_TEST_NEXT_MAJOR: '1'
# These two variables allow PHPUnit to run in a parallel job matrix.
_PHPUNIT_CONCURRENT: '1'
......@@ -59,6 +67,49 @@ variables:
OPT_IN_TEST_NEXT_MINOR: 1
OPT_IN_TEST_MAX_PHP: 1
# @todo Remove when https://www.drupal.org/project/gitlab_templates/issues/3452183 lands.
.is-push-rule: &is-push-rule
if: $CI_PIPELINE_SOURCE == "push" && $CI_PROJECT_ROOT_NAMESPACE == "project"
.manual-rule: &manual-rule
- when: manual
allow_failure: true
# @todo Remove when https://www.drupal.org/project/gitlab_templates/issues/3452183 lands.
.php-files-change-rule: &php-files-change-rule
- changes:
- "**/*.{php,module,inc,install,theme,profile}"
when: on_success
# @todo Remove when https://www.drupal.org/project/gitlab_templates/issues/3452183 lands.
.css-files-change-rule: &css-files-change-rule
- changes:
- "**/*.css"
when: on_success
# @todo Remove when https://www.drupal.org/project/gitlab_templates/issues/3452183 lands.
.module-metadata-files-change-rule: &module-metadata-files-change-rule
- changes:
- "**/*.yml"
when: on_success
# @todo Remove when https://www.drupal.org/project/gitlab_templates/issues/3452183 lands.
.module-config-files-change-rule: &module-config-files-change-rule
- changes:
# config schema and/or default config
- config
when: on_success
# @todo Remove when https://www.drupal.org/project/gitlab_templates/issues/3452183 lands.
composer-lint:
rules:
- *is-push-rule
- *php-files-change-rule
- changes:
- composer.json
when: on_success
- *manual-rule
################
# Require composer checks to pass.
################
......@@ -84,6 +135,50 @@ stylelint:
################
phpcs:
allow_failure: false
# @todo Remove when https://www.drupal.org/project/gitlab_templates/issues/3452183 lands.
rules:
- *is-push-rule
- *php-files-change-rule
# Also run whenever we catch up to the upstream PHPCS rules.
- changes:
- core.phpcs.xml.dist
when: on_success
- *manual-rule
phpcs-rules-match-drupal-11:
stage: development
script:
- |
# Download the file from the Drupal repository
curl -o drupal_core_phpcs.xml.dist https://git.drupalcode.org/project/drupal/-/raw/11.x/core/phpcs.xml.dist?ref_type=heads
# Check if the download was successful
if [ $? -ne 0 ]; then
echo "🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥"
echo "Error: Failed to download $DRUPAL_URL"
exit 1
fi
# Compare the downloaded file with the local file using a subshell to prevent premature exit
echo "Comparing downloaded file with local file..."
# With uses the braces the job just ends here for some reason.
{
diff drupal_core_phpcs.xml.dist core.phpcs.xml.dist
DIFF_EXIT_CODE=$?
} || true
if [ $DIFF_EXIT_CODE -ne 0 ]; then
echo "🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥🔥"
echo "Error: core.phpcs.xml.dist not up to date with Drupal 11.x core/phpcs.xml.dist. Please create an issue to update this file."
else
echo "👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍👍"
echo "core.phpcs.xml.dist is up to date with Drupal 11.x core/phpcs.xml.dist"
fi
# Exit with the exit code of the diff command
exit $DIFF_EXIT_CODE
# Allow this to fail, to avoid disrupting XB's CI when upstream changes.
allow_failure: true
################
# Require phpstan checks to pass.
......@@ -99,6 +194,7 @@ eslint:
allow_failure: false
before_script:
- rm -fr sveltejs
- rm -fr recipes
################
# Add a new section for validation of Svelte JS.
......@@ -176,3 +272,12 @@ phpunit:
phpunit (next major):
# Require us to be compatible with the next major version of core.
allow_failure: false
phpstan (previous major):
extends: phpstan
rules:
- !reference [ .opt-in-previous-major-rule ]
- !reference [ .skip-phpstan-rule ]
- !reference [ .phpstan-allow-failure-rule ]
needs:
- 'composer (previous major)'
......@@ -15,7 +15,7 @@ Our goal is to make it easier to find and install modules for people new to Drup
This module requires no modules outside of Drupal core.
## Installation
## Installation
*If you intend to contribute to Project Browser, skip this step and use the "Contributing" instructions instead*
......@@ -28,7 +28,7 @@ Install with composer: `composer require drupal/project_browser` then enable the
) to clone project browser to your site
- In the `/project_browser` directory, install PHP dependencies with `composer install`
- In the `/project_browser/sveltejs` directory:
- install JS dependencies with `yarn install`
- install JS dependencies with `yarn install`
- For development, run the dev script `yarn dev` which will watch for filesystem changes
- Note: `yarn dev` will report the app is available localhost, but it is fully available in your Drupal site at `admin/modules/browse`
- When you are done, compile the changes with `yarn build`
......@@ -43,6 +43,16 @@ Filter by Recommended projects or All projects
Search and filter by Title, Sort By, Order and Categories
Customize results layout by List or Grid Format
## Updating fixtures for Drupal.org JSON:API
The tests in `tests/src/FunctionalJavascript/ProjectBrowserUiTestJsonApi.php` use data from local fixtures stored in the `tests/fixtures/drupalorg_jsonapi` folder instead of actual API endpoints.
`DrupalOrgClientMiddleware.php` contains the mapping from request to fixture file.
If this data needs to change, just change the queries or mapping in the above file and run the PHP script to regenerate the fixtures:
```
php scripts/regenerate-drupalorg-jsonapi-fixture.php
```
## Maintainers
......
......@@ -14,10 +14,11 @@
"require-dev": {
"colinodell/psr-testlogger": "^1.2",
"drupal/automatic_updates": "^3.1.3",
"kanopi/imagemagick-configuration": "@dev"
"drush/drush": "^12.5 || ^13"
},
"conflict": {
"drupal/automatic_updates": "<3.0"
"drupal/automatic_updates": "<3.0",
"drupal/gin": "<4.0.6"
},
"config": {
"optimize-autoloader": true,
......
enabled_sources:
- drupalorg_jsonapi
- recipes
allow_ui_install: false
allowed_projects: {}
max_selections: null
......@@ -22,3 +22,47 @@ project_browser.admin_settings:
label: 'Project identifier'
constraints:
NotBlank: []
max_selections:
type: integer
nullable: true
label: 'The maximum number of projects that can be chosen for installation at once. Internal use only.'
block.settings.project_browser_block:*:
type: block_settings
label: 'Project Browser block settings'
constraints:
FullyValidatable: ~
mapping:
paginate:
type: boolean
label: 'Enable pagination'
page_sizes:
type: string
label: 'Page sizes'
constraints:
# A comma-separated list of numbers with fuzzy spacing, e.g. `32, 64,88`.
Regex: '/^([0-9]+\s*,?\s*)+$/'
sort_options:
type: sequence
label: 'Sort options'
nullable: true
sequence:
type: string
label: 'Sort option ID'
constraints:
NotBlank: []
default_sort:
type: string
label: 'Default sort criterion'
nullable: true
constraints:
NotBlank: []
filters:
type: sequence
label: 'Enabled filters'
nullable: true
sequence:
type: string
label: 'Filter ID'
constraints:
NotBlank: []
<?xml version="1.0" encoding="UTF-8"?>
<ruleset name="drupal_core">
<arg name="extensions" value="engine,inc,install,module,php,profile,test,theme,yml"/>
<description>Default PHP CodeSniffer configuration for Drupal core.</description>
<!--Exclude folders used by common frontend tools. These folders match the file_scan_ignore_directories setting in default.settings.php-->
<exclude-pattern>*/bower_components/*</exclude-pattern>
<exclude-pattern>*/node_modules/*</exclude-pattern>
<!--Exclude third party code.-->
<exclude-pattern>./assets/vendor/*</exclude-pattern>
<!--Exclude the PHPStan baseline and temp dir from coding standards.-->
<exclude-pattern>./core/.phpstan-baseline.php</exclude-pattern>
<!-- Exclude third-party code maintained within core that does not follow our standards. -->
<!-- @todo This rule may be removed when https://www.drupal.org/node/1848264 is resolved. -->
<exclude-pattern>./core/lib/Drupal/Component/Diff/</exclude-pattern>
<exclude-pattern>./core/phpstan-tmp/*</exclude-pattern>
<exclude-pattern>./core/tests/Drupal/Tests/Component/Annotation/Doctrine/</exclude-pattern>
<!--Exclude test files that are intentionally empty, or intentionally violate coding standards.-->
<exclude-pattern>./modules/system/tests/fixtures/HtaccessTest</exclude-pattern>
<file>.</file>
<file>../composer</file>
<file>scripts/password-hash.sh</file>
<file>scripts/rebuild_token_calculator.sh</file>
<file>scripts/run-tests.sh</file>
<file>scripts/update-countries.sh</file>
<!-- Only include specific sniffs that pass. This ensures that, if new sniffs are added, HEAD does not fail.-->
<!-- Drupal sniffs -->
<rule ref="Drupal.Arrays.Array"/>
<rule ref="Drupal.Arrays.Array.LongLineDeclaration">
<include-pattern>core/*/Unit/*</include-pattern>
<include-pattern>core/*/FunctionalJavascript/*</include-pattern>
<include-pattern>core/*/Kernel/*</include-pattern>
<include-pattern>core/lib/*</include-pattern>
<include-pattern>core/modules/*/tests/modules/*</include-pattern>
</rule>
<rule ref="Drupal.CSS.ClassDefinitionNameSpacing"/>
<rule ref="Drupal.CSS.ColourDefinition"/>
<rule ref="Drupal.Classes.ClassCreateInstance"/>
<rule ref="Drupal.Classes.ClassDeclaration"/>
<rule ref="Drupal.Classes.ClassFileName"/>
<rule ref="Drupal.Classes.FullyQualifiedNamespace"/>
<rule ref="Drupal.Classes.InterfaceName"/>
<rule ref="Drupal.Classes.PropertyDeclaration"/>
<rule ref="Drupal.Classes.UnusedUseStatement"/>
<rule ref="Drupal.Classes.UseGlobalClass"/>
<rule ref="Drupal.Classes.UseLeadingBackslash"/>
<rule ref="Drupal.Commenting.ClassComment"/>
<rule ref="Drupal.Commenting.DataTypeNamespace"/>
<rule ref="Drupal.Commenting.Deprecated"/>
<rule ref="Drupal.Commenting.DocComment">
<!-- Sniff for these errors: SpacingAfterTagGroup, WrongEnd, SpacingBetween,
ContentAfterOpen, SpacingBeforeShort, TagValueIndent, ShortStartSpace,
SpacingAfter, LongNotCapital, ShortFullStop, TagGroupSpacing, Empty,
TagsNotGrouped, ParamGroup -->
<exclude name="Drupal.Commenting.DocComment.LongFullStop"/>
<exclude name="Drupal.Commenting.DocComment.MissingShort"/>
</rule>
<rule ref="Drupal.Commenting.DocCommentAlignment"/>
<rule ref="Drupal.Commenting.DocCommentLongArraySyntax"/>
<rule ref="Drupal.Commenting.DocCommentStar"/>
<rule ref="Drupal.Commenting.FileComment"/>
<rule ref="Drupal.Commenting.FunctionComment"/>
<rule ref="Drupal.Commenting.FunctionComment.Missing">
<include-pattern>core/*/src/Plugin/*</include-pattern>
<include-pattern>*/Database/*</include-pattern>
<include-pattern>*/FunctionalJavascript/*</include-pattern>
<include-pattern>*/Kernel/*</include-pattern>
<include-pattern>*/Functional/*</include-pattern>
<include-pattern>*/Unit/*</include-pattern>
</rule>
<rule ref="Drupal.Commenting.FunctionComment.MissingParamType"/>
<rule ref="Drupal.Commenting.FunctionComment.MissingReturnComment">
<include-pattern>core/lib/Drupal/Core/*</include-pattern>
<include-pattern>core/lib/Drupal/Component/*</include-pattern>
<include-pattern>core/tests/*</include-pattern>
<include-pattern>core/*/tests/*</include-pattern>
</rule>
<rule ref="Drupal.Commenting.GenderNeutralComment"/>
<rule ref="Drupal.Commenting.HookComment"/>
<rule ref="Drupal.Commenting.InlineComment">
<!-- Sniff for: NoSpaceBefore, SpacingBefore, WrongStyle -->
<exclude name="Drupal.Commenting.InlineComment.InvalidEndChar"/>
<exclude name="Drupal.Commenting.InlineComment.SpacingAfter"/>
</rule>
<rule ref="Drupal.Commenting.InlineVariableComment"/>
<rule ref="Drupal.Commenting.PostStatementComment"/>
<rule ref="Drupal.Commenting.TodoComment" />
<rule ref="Drupal.Commenting.VariableComment"/>
<rule ref="Drupal.ControlStructures.ControlSignature"/>
<rule ref="Drupal.ControlStructures.ElseIf"/>
<rule ref="Drupal.ControlStructures.InlineControlStructure"/>
<rule ref="Drupal.Files.EndFileNewline"/>
<rule ref="Drupal.Files.FileEncoding"/>
<rule ref="Drupal.Files.LineLength">
<include-pattern>*/core/lib/*</include-pattern>
<exclude-pattern>*/core/lib/*/*Trait</exclude-pattern>
<include-pattern>*/core/includes/*</include-pattern>
<include-pattern>*/core/profiles/*</include-pattern>
<include-pattern>*/core/recipes/*</include-pattern>
<include-pattern>*/core/themes/*</include-pattern>
</rule>
<rule ref="Drupal.Files.TxtFileLineLength"/>
<rule ref="Drupal.Formatting.MultiLineAssignment"/>
<rule ref="Drupal.Formatting.MultipleStatementAlignment"/>
<rule ref="Drupal.Formatting.SpaceInlineIf"/>
<rule ref="Drupal.Formatting.SpaceUnaryOperator"/>
<rule ref="Drupal.Functions.DiscouragedFunctions"/>
<rule ref="Drupal.Functions.FunctionDeclaration"/>
<rule ref="Drupal.Functions.MultiLineFunctionDeclaration"/>
<rule ref="Drupal.InfoFiles.AutoAddedKeys"/>
<rule ref="Drupal.InfoFiles.ClassFiles"/>
<rule ref="Drupal.InfoFiles.DuplicateEntry"/>
<rule ref="Drupal.InfoFiles.Required"/>
<rule ref="Drupal.Methods.MethodDeclaration">
<!-- Silence method name underscore warning which is covered already in
Drupal.NamingConventions.ValidFunctionName.ScopeNotCamelCaps. -->
<exclude name="Drupal.Methods.MethodDeclaration.Underscore"/>
</rule>
<rule ref="Drupal.NamingConventions.ValidClassName"/>
<rule ref="Drupal.NamingConventions.ValidGlobal"/>
<rule ref="Drupal.NamingConventions.ValidVariableName"/>
<rule ref="Drupal.NamingConventions.ValidVariableName.LowerCamelName"/>
<rule ref="Drupal.Scope.MethodScope"/>
<rule ref="Drupal.Semantics.ConstantName.ConstantStart"/>
<rule ref="Drupal.Semantics.EmptyInstall"/>
<rule ref="Drupal.Semantics.FunctionAlias"/>
<rule ref="Drupal.Semantics.FunctionT"/>
<rule ref="Drupal.Semantics.FunctionTriggerError"/>
<rule ref="Drupal.Semantics.FunctionWatchdog"/>
<rule ref="Drupal.Semantics.InstallHooks"/>
<rule ref="Drupal.Semantics.LStringTranslatable"/>
<rule ref="Drupal.Semantics.PregSecurity"/>
<rule ref="Drupal.Semantics.RemoteAddress"/>
<rule ref="Drupal.Semantics.TInHookMenu"/>
<rule ref="Drupal.Semantics.TInHookSchema"/>
<rule ref="Drupal.Strings.UnnecessaryStringConcat"/>
<rule ref="Drupal.WhiteSpace.CloseBracketSpacing"/>
<rule ref="Drupal.WhiteSpace.Comma"/>
<rule ref="Drupal.WhiteSpace.EmptyLines"/>
<rule ref="Drupal.WhiteSpace.Namespace"/>
<rule ref="Drupal.WhiteSpace.ObjectOperatorIndent"/>
<rule ref="Drupal.WhiteSpace.ObjectOperatorSpacing"/>
<rule ref="Drupal.WhiteSpace.OpenBracketSpacing"/>
<rule ref="Drupal.WhiteSpace.OpenTagNewline"/>
<rule ref="Drupal.WhiteSpace.ScopeClosingBrace"/>
<rule ref="Drupal.WhiteSpace.ScopeIndent"/>
<!-- Drupal Practice sniffs -->
<rule ref="DrupalPractice.CodeAnalysis.VariableAnalysis">
<!-- Do not run this sniff on API files or transliteration data. -->
<exclude-pattern>*.api.php</exclude-pattern>
<exclude-pattern>core/lib/Drupal/Component/Transliteration/data/*.php</exclude-pattern>
<properties>
<property name="allowUnusedFunctionParameters" value="true"/>
</properties>
</rule>
<rule ref="DrupalPractice.CodeAnalysis.VariableAnalysis.UndefinedUnsetVariable">
<severity>0</severity>
</rule>
<rule ref="DrupalPractice.CodeAnalysis.VariableAnalysis.UndefinedVariable">
<!-- Setting severity to 0 to completely disable an error message in this sniff, without excluding the whole sniff -->
<!-- See https://github.com/squizlabs/PHP_CodeSniffer/wiki/Configuration-Options#changing-the-default-severity-levels -->
<severity>0</severity>
</rule>
<rule ref="DrupalPractice.Commenting.ExpectedException"/>
<rule ref="DrupalPractice.General.ExceptionT"/>
<rule ref="DrupalPractice.InfoFiles.NamespacedDependency"/>
<rule ref="DrupalPractice.Objects.GlobalFunction"/>
<!-- Generic sniffs -->
<rule ref="Generic.Arrays.DisallowLongArraySyntax"/>
<rule ref="Generic.CodeAnalysis.EmptyPHPStatement"/>
<rule ref="Generic.Files.ByteOrderMark"/>
<rule ref="Generic.Files.LineEndings"/>
<rule ref="Generic.Formatting.DisallowMultipleStatements"/>
<rule ref="Generic.Formatting.SpaceAfterCast"/>
<rule ref="Generic.Functions.FunctionCallArgumentSpacing"/>
<rule ref="Generic.NamingConventions.ConstructorName"/>
<rule ref="Generic.NamingConventions.UpperCaseConstantName"/>
<rule ref="Generic.PHP.DeprecatedFunctions"/>
<rule ref="Generic.PHP.DisallowShortOpenTag"/>
<rule ref="Generic.PHP.LowerCaseKeyword"/>
<rule ref="Generic.PHP.UpperCaseConstant"/>
<rule ref="Generic.WhiteSpace.DisallowTabIndent"/>
<!-- Internal sniffs -->
<rule ref="Internal.NoCodeFound">
<!-- No PHP code in *.yml -->
<exclude-pattern>*.yml</exclude-pattern>
</rule>
<!-- MySource sniffs -->
<rule ref="MySource.Debug.DebugCode"/>
<!-- PEAR sniffs -->
<rule ref="PEAR.Files.IncludingFile"/>
<!-- Disable some error messages that we do not want. -->
<rule ref="PEAR.Files.IncludingFile.UseInclude">
<severity>0</severity>
</rule>
<rule ref="PEAR.Files.IncludingFile.UseIncludeOnce">
<severity>0</severity>
</rule>
<rule ref="PEAR.Files.IncludingFile.UseRequire">
<severity>0</severity>
</rule>
<rule ref="PEAR.Files.IncludingFile.UseRequireOnce">
<severity>0</severity>
</rule>
<rule ref="PEAR.Functions.FunctionCallSignature"/>
<!-- The sniffs inside PEAR.Functions.FunctionCallSignature silenced below are
also silenced in Drupal CS' ruleset.xml. The code below is a 1-on-1 copy
from that file. -->
<rule ref="PEAR.Functions.FunctionCallSignature.CloseBracketLine">
<severity>0</severity>
</rule>
<rule ref="PEAR.Functions.FunctionCallSignature.ContentAfterOpenBracket">
<severity>0</severity>
</rule>
<rule ref="PEAR.Functions.FunctionCallSignature.EmptyLine">
<severity>0</severity>
</rule>
<!-- Disable some error messages that we do not want. -->
<rule ref="PEAR.Functions.FunctionCallSignature.Indent">
<severity>0</severity>
</rule>
<rule ref="PEAR.Functions.FunctionCallSignature.OpeningIndent">
<severity>0</severity>
</rule>
<!-- Disable some error messages that we already cover. -->
<rule ref="PEAR.Functions.FunctionCallSignature.SpaceAfterOpenBracket">
<severity>0</severity>
</rule>
<rule ref="PEAR.Functions.FunctionCallSignature.SpaceBeforeCloseBracket">
<severity>0</severity>
</rule>
<rule ref="PEAR.Functions.ValidDefaultValue"/>
<!-- PSR-2 sniffs -->
<rule ref="PSR2.Classes.PropertyDeclaration">
<!-- Silence method name underscore warning which is covered already in
Drupal.Classes.PropertyDeclaration. -->
<exclude name="PSR2.Classes.PropertyDeclaration.Underscore"/>
</rule>
<rule ref="PSR2.Namespaces.NamespaceDeclaration"/>
<rule ref="PSR2.Namespaces.UseDeclaration"/>
<!-- SlevomatCodingStandard sniffs -->
<rule ref="SlevomatCodingStandard.Classes.BackedEnumTypeSpacing"/>
<rule ref="SlevomatCodingStandard.Commenting.ForbiddenAnnotations">
<properties>
<property name="forbiddenAnnotations" type="array">
<element value="@inheritDoc"/>
<element value="@inheritdoc"/>
<element value="@returns"/>
</property>
</properties>
</rule>
<rule ref="SlevomatCodingStandard.Commenting.ForbiddenComments">
<properties>
<property name="forbiddenCommentPatterns" type="array">
<element value="/@inheritDoc/"/>
</property>
</properties>
</rule>
<rule ref="SlevomatCodingStandard.ControlStructures.RequireNullCoalesceOperator"/>
<rule ref="SlevomatCodingStandard.ControlStructures.RequireShortTernaryOperator"/>
<rule ref="SlevomatCodingStandard.Exceptions.RequireNonCapturingCatch" />
<rule ref="SlevomatCodingStandard.TypeHints.DeclareStrictTypes">
<properties>
<property name="spacesCountAroundEqualsSign" value="0" />
</properties>
<include-pattern>*/tests/*</include-pattern>
<exclude-pattern>*/fixtures/*</exclude-pattern>
</rule>
<rule ref="SlevomatCodingStandard.TypeHints.NullableTypeForNullDefaultValue"/>
<!-- Squiz sniffs -->
<rule ref="Squiz.Arrays.ArrayBracketSpacing"/>
<rule ref="Squiz.Arrays.ArrayDeclaration">
<exclude name="Squiz.Arrays.ArrayDeclaration.KeySpecified"/>
<exclude name="Squiz.Arrays.ArrayDeclaration.NoKeySpecified"/>
</rule>
<!-- Disable some error messages that we do not want. -->
<rule ref="Squiz.Arrays.ArrayDeclaration.CloseBraceNotAligned">
<severity>0</severity>
</rule>
<rule ref="Squiz.Arrays.ArrayDeclaration.DoubleArrowNotAligned">
<severity>0</severity>
</rule>
<rule ref="Squiz.Arrays.ArrayDeclaration.FirstValueNoNewline">
<severity>0</severity>
</rule>
<rule ref="Squiz.Arrays.ArrayDeclaration.KeyNotAligned">
<severity>0</severity>
</rule>
<rule ref="Squiz.Arrays.ArrayDeclaration.MultiLineNotAllowed">
<severity>0</severity>
</rule>
<rule ref="Squiz.Arrays.ArrayDeclaration.NoComma">
<severity>0</severity>
</rule>
<rule ref="Squiz.Arrays.ArrayDeclaration.NoCommaAfterLast">
<severity>0</severity>
</rule>
<rule ref="Squiz.Arrays.ArrayDeclaration.NotLowerCase">
<severity>0</severity>
</rule>
<rule ref="Squiz.Arrays.ArrayDeclaration.SingleLineNotAllowed">
<severity>0</severity>
</rule>
<rule ref="Squiz.Arrays.ArrayDeclaration.ValueNoNewline">
<severity>0</severity>
</rule>
<rule ref="Squiz.Arrays.ArrayDeclaration.ValueNotAligned">
<severity>0</severity>
</rule>
<rule ref="Squiz.ControlStructures.ForEachLoopDeclaration"/>
<!-- Disable some error messages that we already cover. -->
<rule ref="Squiz.ControlStructures.ForEachLoopDeclaration.AsNotLower">
<severity>0</severity>
</rule>
<rule ref="Squiz.ControlStructures.ForEachLoopDeclaration.SpaceAfterOpen">
<severity>0</severity>
</rule>
<rule ref="Squiz.ControlStructures.ForEachLoopDeclaration.SpaceBeforeClose">
<severity>0</severity>
</rule>
<rule ref="Squiz.ControlStructures.ForLoopDeclaration"/>
<!-- Disable some error messages that we already cover. -->
<rule ref="Squiz.ControlStructures.ForLoopDeclaration.SpacingAfterOpen">
<severity>0</severity>
</rule>
<rule ref="Squiz.ControlStructures.ForLoopDeclaration.SpacingBeforeClose">
<severity>0</severity>
</rule>
<rule ref="Squiz.ControlStructures.SwitchDeclaration"/>
<!-- Disable some error messages that we do not want. -->
<rule ref="Squiz.ControlStructures.SwitchDeclaration.BreakIndent">
<severity>0</severity>
</rule>
<rule ref="Squiz.ControlStructures.SwitchDeclaration.CaseIndent">
<severity>0</severity>
</rule>
<rule ref="Squiz.ControlStructures.SwitchDeclaration.CloseBraceAlign">
<severity>0</severity>
</rule>
<rule ref="Squiz.ControlStructures.SwitchDeclaration.DefaultIndent">
<severity>0</severity>
</rule>
<rule ref="Squiz.ControlStructures.SwitchDeclaration.DefaultNoBreak">
<severity>0</severity>
</rule>
<rule ref="Squiz.ControlStructures.SwitchDeclaration.EmptyCase">
<severity>0</severity>
</rule>
<rule ref="Squiz.ControlStructures.SwitchDeclaration.EmptyDefault">
<severity>0</severity>
</rule>
<rule ref="Squiz.ControlStructures.SwitchDeclaration.MissingDefault">
<severity>0</severity>
</rule>
<rule ref="Squiz.ControlStructures.SwitchDeclaration.SpacingAfterCase">
<severity>0</severity>
</rule>
<rule ref="Squiz.ControlStructures.SwitchDeclaration.SpacingAfterDefaultBreak">
<severity>0</severity>
</rule>
<rule ref="Squiz.ControlStructures.SwitchDeclaration.SpacingBeforeBreak">
<severity>0</severity>
</rule>
<rule ref="Squiz.Functions.FunctionDeclarationArgumentSpacing">
<properties>
<property name="equalsSpacing" value="1"/>
</properties>
</rule>
<rule ref="Squiz.Functions.FunctionDeclarationArgumentSpacing.NoSpaceBeforeArg">
<severity>0</severity>
</rule>
<rule ref="Squiz.PHP.LowercasePHPFunctions"/>
<rule ref="Squiz.PHP.NonExecutableCode"/>
<rule ref="Squiz.Strings.ConcatenationSpacing">
<properties>
<property name="spacing" value="1"/>
<property name="ignoreNewlines" value="true"/>
</properties>
</rule>
<rule ref="Squiz.WhiteSpace.FunctionSpacing">
<properties>
<property name="spacing" value="1"/>
</properties>
</rule>
<rule ref="Squiz.WhiteSpace.LanguageConstructSpacing"/>
<rule ref="Squiz.WhiteSpace.OperatorSpacing">
<properties>
<property name="ignoreNewlines" value="true"/>
</properties>
</rule>
<rule ref="Squiz.WhiteSpace.ScopeKeywordSpacing"/>
<rule ref="Squiz.WhiteSpace.SemicolonSpacing"/>
<rule ref="Squiz.WhiteSpace.SuperfluousWhitespace"/>
<!-- Zend sniffs -->
<rule ref="Zend.Files.ClosingTag"/>
</ruleset>
/* cspell:ignore xxxs */
#project-browser .search__form-container,
#project-browser .pb-layout,
#project-browser .pb-module-page {
margin-block-start: 0;
padding: var(--gin-spacing-s);
border: 1px solid var(--gin-border-color-layer);
border-radius: var(--gin-border-l);
background: var(--gin-bg-layer);
box-shadow: var(--gin-shadow-l1);
}
@media (min-width: 48em) {
#project-browser .search__form-container,
#project-browser .pb-layout,
#project-browser .pb-module-page {
padding: var(--gin-spacing-l);
}
}
.gin--dark-mode #project-browser .search__form-container,
.gin--dark-mode #project-browser .pb-layout,
.gin--dark-mode #project-browser .pb-module-page {
box-shadow: 0 6px 16px var(--gin-border-color-layer);
}
.gin--high-contrast-mode #project-browser .search__form-container,
.gin--high-contrast-mode #project-browser .pb-layout,
.gin--high-contrast-mode #project-browser .pb-module-page {
box-shadow: none;
}
#project-browser .search__form-container {
margin-block-start: var(--gin-spacing-l);
}
#project-browser .search__search-bar {
border: none;
background: none;
}
#project-browser .search__search-bar .search__search_term {
padding: var(--gin-spacing-m);
border: 1px solid var(--gin-border-color-form-element);
}
#project-browser .search__search-icon {
width: 16px;
height: 16px;
inset-inline-end: 20px;
inset-block-end: 20px;
}
#project-browser .search__search-clear {
z-index: 2;
display: flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
margin-right: 10px;
padding: 0;
border-radius: 50%;
background: var(--gin-status-bg);
inset-block-start: 17px;
inset-inline-end: 16px;
}
#project-browser .search__search-clear img {
width: 12px;
height: 12px;
}
.gin--dark-mode #project-browser .search__search-clear img {
filter: brightness(0) invert(1);
}
#project-browser .search__search-clear + .search__search-icon {
display: none;
}
#project-browser .search__form-item {
margin-block-start: 0;
margin-block-end: var(--gin-spacing-s);
}
#project-browser .search__results-count {
display: flex;
flex-wrap: wrap;
gap: var(--gin-spacing-xs);
}
#project-browser .search__form-filters-container {
padding: var(--gin-spacing-m);
border: 1px solid var(--gin-border-color);
border-radius: var(--gin-border-m);
background: none;
}
#project-browser .search__form-filters {
align-items: baseline;
}
#project-browser .search__form-filters .form-item {
padding-block-start: 0;
margin-block: 0 var(--gin-spacing-s);
}
#project-browser .search__grid-container,
#project-browser .search__dropdown.dropdown-filters {
border: none;
background-color: transparent;
}
#project-browser .search__grid-container {
grid-gap: 0;
padding: 0;
}
#project-browser .search__filters {
margin-block-start: 0;
padding: 0;
background: none;
}
#project-browser .search__filter-wrapper {
gap: var(--gin-spacing-s);
margin-block-start: var(--gin-spacing-s);
}
@media (min-width: 48em) {
#project-browser .search__filter-wrapper {
margin-block-start: 0;
}
}
#project-browser .search__filter__toggle-container {
margin-inline-end: 0;
}
#project-browser .search__filter-button {
color: var(--gin-color-primary);
font-size: var(--gin-font-size-xs);
}
#project-browser .search__filter__toggle.form-element,
#project-browser .search__sort-select.form-element {
margin-block-start: 0;
margin-inline-start: 0;
transition: var(--gin-transition);
text-decoration: none;
color: var(--gin-color-text);
border: 1px solid var(--gin-border-color-form-element);
border-radius: var(--gin-border-m);
background-color: var(--gin-bg-input);
}
#project-browser .search__checkbox-label {
font-size: var(--gin-font-size-s);
}
#project-browser
.search__filter__toggle.form-element
.search__filter__toggle-img {
padding: 0;
}
.gin--dark-mode
#project-browser
.search__filter__toggle.form-element
.search__filter__toggle-img {
filter: invert(1);
}
#project-browser .search__sort {
display: flex;
align-items: center;
gap: var(--gin-spacing-xs);
}
#project-browser .search__sort label {
flex-shrink: 0;
}
#project-browser .pb-display {
gap: var(--gin-spacing-xxxs);
margin-block-end: 0;
margin-inline-end: 0;
padding: var(--gin-spacing-xxs);
border: 1px solid var(--gin-border-color);
border-radius: var(--gin-spacing-xl);
background: var(--gin-bg-layer);
}
#project-browser .pb-display__button {
padding: var(--gin-spacing-xs) var(--gin-spacing-s);
color: var(--gin-color-text);
border-radius: var(--gin-border-xl);
background: var(--gin-bg-layer);
box-shadow: none;
font-size: var(--gin-font-size-s);
}
#project-browser .pb-display__button:hover {
z-index: 2;
color: var(--gin-color-primary);
background: var(--gin-color-primary-light);
}
#project-browser .pb-display__button:active,
#project-browser .pb-display__button:focus,
#project-browser .pb-display__button--selected {
z-index: 2;
color: var(--gin-color-button-text);
background: var(--gin-color-primary);
}
#project-browser .pb-display__button:active img,
#project-browser .pb-display__button:focus img,
#project-browser .pb-display__button--selected img {
filter: invert(1);
}
.gin--dark-mode #project-browser .pb-display__button:active img,
.gin--dark-mode #project-browser .pb-display__button:focus img,
.gin--dark-mode #project-browser .pb-display__button--selected img {
filter: none;
}
#project-browser .pb-display__button:focus {
box-shadow:
0 0 0 1px var(--gin-color-focus-border),
0 0 0 4px var(--gin-color-focus);
}
.gin--dark-mode #project-browser .pb-display__button-icon {
filter: invert(1);
}
#project-browser .pb-layout__header {
align-items: baseline;
margin-block: var(--gin-spacing-l) var(--gin-spacing-s);
border-block-end: none;
}
#project-browser .filter-group__title-wrapper,
#project-browser .pb-filter__heading {
margin-block-end: 0;
color: var(--gin-color-title);
font-size: var(--gin-font-size-s);
font-weight: var(--gin-font-weight-bold);
}
#project-browser .pb-filter__heading {
display: inline-flex;
}
@media (min-width: 61em) {
#project-browser .pb-filter__heading {
margin-block-end: var(--gin-spacing-s);
}
}
#project-browser .pb-filter__summary {
padding: var(--gin-spacing-s);
border-radius: var(--gin-border-m);
background: var(--gin-bg-layer2);
}
#project-browser .pb-filter__checkbox-label {
padding-block-start: 0;
font-size: var(--gin-font-size-s);
}
#project-browser .filter-applied {
min-height: 2rem;
margin: 0;
color: var(--gin-color-primary-active);
background: var(--gin-color-primary-light);
font-size: var(--gin-font-size-xs);
font-weight: var(--gin-font-weight-semibold);
padding-block: 0;
padding-inline: var(--gin-spacing-s) var(--gin-spacing-xs);
}
.gin--dark-mode #project-browser .filter-applied img {
filter: invert(1);
}
.pb-project.pb-project--grid,
.pb-project.pb-project--list {
border: 1px solid var(--gin-border-color-layer2);
border-radius: var(--gin-border-m);
background: transparent;
box-shadow: none;
}
.gin--dark-mode .pb-project.pb-project--grid,
.gin--dark-mode .pb-project.pb-project--list {
background: var(--gin-bg-layer2);
}
.pb-project.pb-project--grid .pb-project__logo,
.pb-project.pb-project--list .pb-project__logo {
line-height: 0;
}
.pb-project.pb-project--grid .pb-project__logo-image,
.pb-project.pb-project--list .pb-project__logo-image {
width: 64px;
}
.pb-project.pb-project--grid .pb-project__title,
.pb-project.pb-project--list .pb-project__title {
margin-block-end: var(--gin-spacing-xxs);
font-size: var(--gin-font-size-l);
font-weight: var(--gin-font-weight-bold);
}
.pb-project.pb-project--grid .pb-project__title .pb-project__link,
.pb-project.pb-project--list .pb-project__title .pb-project__link {
text-decoration: none;
color: var(--gin-color-primary);
}
.pb-project.pb-project--grid .pb-project__body,
.pb-project.pb-project--list .pb-project__body {
font-size: var(--gin-font-size-s);
}
.pb-project.pb-project--grid .project__action_button,
.pb-project.pb-project--list .project__action_button {
border-radius: var(--gin-border-s);
font-size: var(--gin-font-size-xxs);
padding-block: calc(var(--gin-spacing-xxs) + 2px);
padding-inline: var(--gin-spacing-s);
}
.pb-project--list {
grid-template-columns: 64px 1fr;
}
.pb-project__image--list {
padding-inline-start: 0;
}
.pb-project__project-usage-container {
gap: var(--gin-spacing-xxxs);
}
.pb-project__status-icon img {
width: 24px;
}
.pb-search-results {
margin: 0;
margin-block-start: var(--gin-spacing-xs);
font-size: var(--gin-font-size-h3);
font-weight: var(--gin-font-weight-bold);
}
.pb-actions .project_status-indicator {
display: inline-flex;
border-radius: var(--gin-border-xl);
font-size: var(--gin-font-size-s);
font-weight: var(--gin-font-weight-bold);
gap: var(--gin-spacing-xs);
padding-block: var(--gin-spacing-xxs);
padding-inline: var(--gin-spacing-xs) var(--gin-spacing-s);
}
.pb-actions__icon {
color: var(--gin-status-success-text);
}
.gin--dark-mode .pb-icon--usage {
filter: invert(1);
}
.pb-icon--module-details {
width: auto;
margin-inline: 0;
}
.gin--dark-mode .pb-icon--status.pb-module-page__module-details-icon,
.gin--dark-mode .pb-project__status-icon img {
mix-blend-mode: plus-lighter;
}
.gin--dark-mode .project-browser-popup .command-box,
.gin--dark-mode .project-browser-popup input {
background: var(--gin-bg-layer2);
}
.search__form .filter-applied,
.pb-project-categories__item,
.pb-module-page__categories-list-item {
margin-inline-end: 0.25em;
border-radius: 2rem;
font-size: var(--gin-font-size-xs);
font-weight: var(--gin-font-weight-semibold);
}
.pb-project-categories__item,
.pb-module-page__categories-list-item {
padding: var(--gin-spacing-xxxs) var(--gin-spacing-xs);
}
.pb-module-page {
display: flex;
flex-wrap: wrap;
gap: var(--gin-spacing-xl);
margin-block-start: var(--gin-spacing-s) !important;
}
@media (min-width: 61em) {
.pb-module-page {
flex-wrap: nowrap;
}
}
.pb-module-page__sidebar,
.pb-module-page__main {
flex-basis: 100%;
padding: var(--gin-spacing-s);
}
@media (min-width: 61em) {
.pb-module-page__sidebar,
.pb-module-page__main {
padding: 0;
}
}
.pb-module-page__main {
overflow: auto;
max-width: 100%;
}
@media (min-width: 61em) {
.pb-module-page__sidebar {
flex-basis: 320px;
}
}
.pb-module-page hr {
margin-block: var(--gin-spacing-s);
}
.pb-module-page__project-logo {
max-width: 96px;
margin-block-end: var(--gin-spacing-xs);
}
.pb-module-page__title {
color: var(--gin-color-title);
font-size: var(--gin-font-size-h1);
font-weight: var(--gin-font-weight-bold);
margin-block: 0;
}
.pb-module-page__author {
margin-block-start: 0;
margin-block-end: var(--gin-spacing-m);
color: var(--gin-color-text-light);
}
.pb-module-page__details-title {
position: absolute !important;
overflow: hidden;
clip: rect(1px, 1px, 1px, 1px);
width: 1px;
height: 1px;
word-wrap: normal;
}
.pb-module-page__module-details {
grid-template-columns: 2rem 2fr;
align-items: start;
}
.pb-module-page__module-details-info {
margin: 0;
margin-block-end: var(--gin-spacing-s);
font-size: var(--gin-font-size-s);
}
.pb-module-page__categories-list {
display: flex;
flex-wrap: wrap;
height: auto;
margin-block-end: var(--gin-spacing-m);
gap: var(--gin-spacing-xs);
}
.pb-module-page__categories-label {
margin-block-end: var(--gin-spacing-xs);
}
.pb-module-page__actions {
margin: var(--gin-spacing-s) 0;
}
.pb-module-page__categories-list-item {
margin: 0;
}
/* Prevent the dropbutton from being misaligned within the row of icons. For some
* reason, this is caused by Gin exacerbating the `inline-flex` display of
* dropbuttons that is defined by core. */
.pb-actions .dropbutton-wrapper {
display: inherit;
/* Move the dropbutton slightly away from the "installed" badge. */
margin-block-start: 4px;
}
.gin--dark-mode .pb-filter__multi-dropdown__items--visible {
background-color: var(--gin-bg-layer);
}
.gin--dark-mode .pb-filter__multi-dropdown__items--visible div:hover {
background-color: var(--gin-bg-layer2);
}
.gin--dark-mode .project_status-indicator__label {
color: var(--gin-status-success-text);
}
[data-gin-accent="blue"] .pb-layout__main .views-bulk-actions {
background: rgba(var(--gin-color-sticky-rgb), 0.9);
}
.gin--dark-mode [data-gin-accent="blue"] .pb-layout__main .views-bulk-actions {
background: rgba(27, 27, 29, 0.8);
}
[data-gin-accent="blue"] .views-bulk-actions__item {
color: var(--gin-color-text-light);
}
.gin--dark-mode button.project_status-indicator.project_status-indicator {
margin-bottom: 5px;
margin-left: 5px;
background: none;
font-size: 16px;
font-weight: 700;
}
/* WIP: Move from the style tags. */
/* <Filter> */
.pb-filter__fieldset {
margin: 0;
padding: 0;
}
.pb-filter__summary {
padding: 0;
padding: 1rem;
cursor: pointer;
}
.pb-filter__summary--open {
padding-bottom: 0.95rem;
}
.pb-filter__checkbox-label {
.pb-filter__heading {
display: inline-block;
margin-top: 0;
margin-bottom: 1rem;
padding: 0 0.5rem;
font-size: 1rem;
}
.pb-filter__multi-dropdown__items div {
display: flex;
align-items: center;
gap: 1rem;
padding-left: 1rem;
border-bottom: 1px solid rgba(212, 212, 218, 0.6);
}
.pb-filter__checkbox-label:hover,
.pb-filter__checkbox-label:focus {
.pb-filter__multi-dropdown__items div:hover,
.pb-filter__multi-dropdown__items div:focus {
background-color: #f3f4f9;
}
.pb-filter__checkbox-label-txt {
.pb-filter__multi-dropdown__items div label {
display: block;
flex-grow: 1;
padding: 1rem 0;
cursor: pointer;
}
.pb-filter__multi-dropdown__items div input {
margin-right: var(--space-s);
}
.pb-filter__fieldset {
padding: 1.5rem 1rem 1rem 0;
border: none;
}
.pb-filter__multi-dropdown {
min-width: 10rem;
}
.pb-filter__multi-dropdown__label {
.pb-filter__multi-dropdown__items label {
position: relative;
display: inline-block;
cursor: pointer;
......@@ -50,9 +64,11 @@
.pb-filter__multi-dropdown__items--hidden {
display: none;
}
.pb-filter__multi-dropdown__items--visible {
position: absolute;
z-index: 100;
/* This needs to be underneath the loading overlay. */
z-index: 90;
top: 47px;
right: 0;
display: block;
......@@ -68,31 +84,37 @@
display: flex;
align-items: center;
width: 100%;
height: 400px;
}
.pb-image-carousel__slide {
min-width: 650px;
min-height: 400px;
margin: 10px;
aspect-ratio: 5 / 3;
max-width: min(66%, 700px);
height: auto;
object-fit: cover;
margin-inline: auto;
}
.pb-image-carousel__btn--right {
transform: rotate(180deg);
}
.pb-image-carousel__btn {
margin: 0 10px;
cursor: pointer;
}
.pb-image-carousel__btn--disabled {
cursor: inherit;
opacity: 0.5;
}
.pb-image-carousel__btn-icon {
width: 50px;
height: 59px;
border: none;
background: transparent;
}
@media only screen and (max-width: 600px) {
.pb-image-carousel__slide {
min-width: 60%;
......@@ -100,71 +122,119 @@
}
}
/* <ModulePage> */
.pb-module-page {
/* <DetailModal> */
.pb-detail-modal {
display: flex;
text-align: start;
flex-direction: column;
gap: var(--space-l);
padding-block-start: var(--space-l);
}
@media (min-width: 61rem) {
.pb-detail-modal {
display: grid;
grid-template-rows: auto 1fr;
grid-template-columns: 2.5fr 1.5fr;
gap: var(--space-l);
}
.pb-detail-modal__sidebar {
grid-row: span 2;
}
}
@media (min-width: 75rem) {
.pb-detail-modal {
grid-template-columns: 3fr 1fr;
padding-block-start: var(--space-xl);
}
}
.pb-module-page__sidebar {
.pb-detail-modal__main {
display: flex;
flex: 1;
flex-direction: column;
padding: 40px;
gap: var(--space-l);
}
.pb-module-page__main {
.pb-detail-modal__sidebar {
display: flex;
flex: 4;
flex-direction: column;
align-items: flex-start;
padding: 40px;
gap: var(--space-xl);
padding: var(--space-l);
background-color: var(--color-gray-050);
}
.pb-module-page__actions {
.pb-detail-modal__actions {
margin: 20px;
}
.pb-module-page__categories-label {
.pb-detail-modal__view-commands {
text-align: center;
}
.pb-detail-modal__sidebar_element:nth-of-type(3)::before {
display: block;
margin: 0 0 var(--space-xl);
content: "";
border-top: 1px solid var(--color-gray-100);
}
.pb-detail-modal__sidebar_element a.project__action_button {
text-decoration: none;
}
.pb-detail-modal__sidebar_element a.project__action_button:hover {
color: #fff;
}
.pb-detail-modal__categories-label {
font-weight: bold;
}
.pb-module-page__categories-list {
display: inline-block;
.pb-detail-modal__categories-list {
display: inline-flex;
gap: var(--space-xs);
width: 100%;
height: 20px;
margin: 0.25em 0 0.25em 0;
margin: 0;
padding: 0;
cursor: pointer;
}
.pb-module-page__categories-list-item {
.pb-detail-modal__categories-list-item {
display: inline-block;
margin-top: 5px;
margin-bottom: 2px;
margin-inline-start: 7px;
padding: 2px 9px;
list-style: none;
color: #4f4f4f;
border-radius: 25px;
background-color: #e5e5e5;
font-size: 0.9em;
font-weight: 600;
}
.pb-module-page__module-details {
.pb-detail-modal__module-details {
display: grid;
grid-template-columns: 0.5fr 2fr;
margin-top: 10px;
line-height: 20px;
}
.pb-module-page__module-details-icon {
.pb-detail-modal__module-details-icon {
margin: 10% 20% 0 40%;
}
.pb-module-page__module-details-info {
.pb-detail-modal__module-details-info {
margin: 5px 15px;
}
.pb-module-page__logo {
.pb-detail-modal__logo {
min-height: 200px;
}
.pb-module-page__carousel {
.pb-detail-modal__carousel-wrapper {
margin-top: 1em;
}
.pb-detail-modal__carousel {
margin: 20px 0;
}
@media only screen and (max-width: 600px) {
.pb-module-page__wrapper {
.pb-detail-modal__wrapper {
flex-direction: column;
}
}
......@@ -174,6 +244,7 @@
.pager__link--forward::after {
margin-inline-start: 0.5rem;
}
.pager__link--backward::before {
margin-inline-end: 0.5rem;
}
......@@ -184,37 +255,43 @@
align-items: center;
justify-content: center;
}
.pagination__num-projects {
margin: 0 10px;
}
@media only screen and (max-width: 32rem) {
.pagination__pager {
flex-direction: column;
}
}
/* /SKIP PAGINATION STYLES. THEY SHOULD COME FROM ADMIN THEME. */
/* <ProjectBrowser> */
.pb-layout__header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.5rem 0;
}
.pb-search-results {
margin-bottom: 5px;
margin-inline-start: 10px;
margin-inline-start: 1.6rem;
font-family: sans-serif;
font-size: 14px;
font-weight: 700;
font-style: normal;
line-height: 21px;
}
.pb-display {
display: flex;
gap: 0.5em;
margin-block-end: 1.5em;
margin-inline-end: 25px;
margin-block-end: 0.5rem;
margin-inline-end: 1.6rem;
font-weight: bold;
}
.pb-display__button {
display: flex;
gap: 0.25rem;
......@@ -229,27 +306,34 @@
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.25);
font-family: inherit;
}
.pb-display__button--first {
border-top-left-radius: 2px;
border-bottom-left-radius: 2px;
}
.pb-display__button--last {
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
}
.pb-display__button--selected {
background-color: #adaeb3;
}
.pb-display__button-icon {
pointer-events: none;
}
.pb-warning {
padding: 1em;
border: 1px solid red;
}
.pb-warning__header {
color: red;
}
.pb-warning__text {
margin: 0.5em 0;
}
......@@ -258,6 +342,7 @@
.pb-display__button {
border: 1px solid;
}
@media (prefers-color-scheme: dark) {
.pb-display__button-icon {
filter: invert(1);
......@@ -269,6 +354,7 @@
.pb-layout {
display: flex;
flex-flow: column nowrap;
gap: var(--grid-layout-gap);
}
@media screen and (min-width: 800px) {
......@@ -280,14 +366,15 @@
.pb-layout__aside {
flex: 0 1 max-content;
}
.pb-layout__main {
flex: 1 1 0;
}
.pb-projects-list,
.pb-projects-grid {
--grid-layout-gap: 1rem;
--grid-item--min-width: 340px;
--grid-layout-gap: 1.5rem;
--grid-item--min-width: 350px;
--gap-count: calc(var(--grid-column-count) - 1);
--total-gap-width: calc(var(--gap-count) * var(--grid-layout-gap));
--grid-item--max-width: calc(
......@@ -306,6 +393,8 @@
.pb-projects-grid {
--grid-column-count: 4;
margin-top: -1px;
}
.pb-projects-list {
......@@ -313,6 +402,9 @@
}
/* <Tabs> */
.pb-tabs__tab button {
cursor: pointer;
}
.pb-tabs__link {
border: none;
background: transparent;
......@@ -323,13 +415,16 @@
.pb-actions {
margin-inline-start: auto;
}
.pb-detail-modal__sidebar .pb-actions {
display: grid;
max-width: fit-content;
margin: auto;
}
.pb-actions .loading__ajax-progress {
display: inline-block;
line-height: 0.75;
}
.pb-actions__icon {
color: #228572;
}
/* <Project/Categories> */
.pb-project-categories__list {
......@@ -339,10 +434,12 @@
margin: 1em 0 0.25em 0;
padding: 0;
}
.pb-project-categories__list::before {
content: "Categories: ";
font-weight: 600;
}
.pb-project-categories__item {
display: inline-block;
margin-top: 2px;
......@@ -351,10 +448,12 @@
list-style: none;
font-size: 15px;
}
.pb-project-categories__item--extra {
border: 1px solid #bbb;
background-color: transparent;
}
@media (forced-colors: active) {
.pb-project-categories__item {
border: 1px solid;
......@@ -362,7 +461,6 @@
}
/* <Project/Image> */
.pb-image-carousel__slide,
.image-carousel__slider-image,
.project__logo-image {
display: block;
......@@ -370,9 +468,9 @@
width: 50%;
object-fit: scale-down;
}
/* Small devices (portrait tablets and large phones, 600px and up) */
@media only screen and (min-width: 600px) {
.pb-image-carousel__slide,
.image-carousel__slider-image,
.project__logo-image {
display: block;
......@@ -392,7 +490,8 @@
display: inline-block;
overflow: hidden;
width: 0;
content: "\2026"; /* ascii code for the ellipsis character */
content: "\2026";
/* ascii code for the ellipsis character */
-webkit-animation: ellipsis steps(4, end) 900ms infinite;
animation: ellipsis steps(4, end) 900ms infinite;
vertical-align: bottom;
......@@ -404,22 +503,33 @@
}
}
/* Popup */
.project-browser-popup {
max-width: 50rem;
}
.project-browser-detail-modal {
max-width: 80rem;
}
/* <Project/Project> */
/* One column card view */
.pb-project {
padding: 1em;
padding: 1.5rem;
}
.pb-project .ajax-progress__throbber {
display: inline-block;
}
.pb-project--grid {
display: flex;
flex-flow: column nowrap;
gap: 1em;
gap: var(--grid-layout-gap);
border: 1px solid rgba(212, 212, 218, 0.8);
border-radius: 2px;
box-shadow: 0 4px 10px rgb(0, 0, 0 / 0.1);
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
}
.pb-project__title {
......@@ -431,21 +541,36 @@
}
.pb-project__link {
cursor: pointer;
text-decoration: underline;
color: black;
border: none;
background: transparent;
font-weight: bold;
line-height: 1.75rem;
}
.pb-project__link:hover {
text-decoration: none;
color: #003ecc;
}
.pb-project__icons {
display: flex;
align-items: center;
gap: 10px;
gap: 0.75rem;
margin-block-start: auto;
}
.pb-project__body {
font-size: 15px;
margin-block: 1.25rem;
font-size: 0.95rem;
}
.pb-project-categories {
margin-block-end: 0.5rem;
}
.pb-project--list {
display: grid;
grid-template-areas:
......@@ -453,12 +578,29 @@
"aside footer";
grid-template-rows: 1fr;
grid-template-columns: 100px 1fr;
gap: 1rem;
gap: var(--grid-layout-gap);
border: 0.5px solid #a4a2a2;
background: #fff;
box-shadow: 0 4px 4px rgba(0, 0, 0, 0.25);
}
.pb-detail-modal__header {
display: grid;
grid-template-rows: 1fr;
grid-template-columns: 200px;
gap: var(--space-l);
}
@media (min-width: 31rem) {
.pb-detail-modal__header {
grid-template-columns: 200px 1fr;
}
}
.pb-detail-modal__title {
align-self: center;
}
.pb-project__image--list {
padding-inline-start: 4em;
}
......@@ -470,33 +612,42 @@
.pb-project__logo--grid {
align-self: center;
}
.pb-project__logo--list {
grid-area: aside;
justify-self: center;
}
.pb-project__logo-image {
display: block;
width: 100px;
margin: 0;
}
.pb-project__main--list {
grid-area: main;
}
/* TODO: project icons warnings stuff. */
.pb-project__icons p {
display: inline;
}
.pb-project__icons.warnings {
display: block;
}
.pb-project__icons.warnings span {
display: list-item;
}
.pb-project__icons.warnings img {
position: relative;
bottom: -0.25rem;
display: inline;
width: 1.2rem;
}
.warnings + .action {
margin-bottom: 1em;
margin-inline-end: 1em;
......@@ -507,15 +658,17 @@
align-items: center;
justify-content: center;
}
.pb-project__status-icon {
display: block;
font-size: 30px; /* Used to set size of icon, which is 1em. */
}
.pb-project__status-icon-btn {
padding: 0;
border: none;
background: none;
}
.pb-project__active-installs-text,
.pb-project__install-count {
font-size: 13px;
......@@ -536,6 +689,7 @@
font-size: 12.65px;
line-height: 19px;
}
@media (forced-colors: active) {
.project_button--primary {
border: 1px solid !important;
......@@ -544,13 +698,16 @@
/* <Project/ProjectIcon> */
.pb-icon {
max-height: 1em;
max-height: 2rem;
}
.pb-icon--project-listing {
width: 2.4em;
}
.pb-icon--module-details {
width: 25px;
vertical-align: middle;
/* width: 25px; */
}
@media (forced-colors: active) and (prefers-color-scheme: dark) {
......@@ -561,8 +718,12 @@
/* <Project/ProjectStatusIndicator> */
.project_status-indicator {
text-decoration: none;
font-weight: bold;
display: flex;
align-items: center;
padding: 0.25rem 0.5rem;
cursor: not-allowed;
border: none;
background: none;
}
/* <Search/FilterApplied> */
......@@ -580,6 +741,7 @@
font-size: 0.875rem;
font-weight: 700;
}
.filter-applied__button-close {
display: inline-flex;
margin-inline-start: 8px;
......@@ -590,6 +752,7 @@
background: none;
box-shadow: none;
}
.filter-applied__button-close > img {
width: 16px;
height: 16px;
......@@ -600,6 +763,7 @@
.filter-applied {
border: 1px solid;
}
@media (prefers-color-scheme: light) {
.filter-applied__button-close > img {
filter: invert(1);
......@@ -611,15 +775,18 @@
.filter-group__radio:hover {
cursor: pointer;
}
.filter-group {
margin: 0;
padding: 0;
border: none;
}
.filter-group__title-wrapper {
margin-block-end: 10px;
font-size: 18px;
}
.filter-group__filter-option {
display: flex;
align-items: center;
......@@ -635,7 +802,7 @@
/* <Search/Search> */
.search__form-container {
margin: -1px 0 2rem;
margin: -1px 0 0.5rem;
padding: 1.5rem;
border: 1px solid #dedfe4;
border-radius: 1px;
......@@ -643,6 +810,10 @@
box-shadow: 0 2px 0.25rem rgba(0, 0, 0, 0.1);
}
.search__form-container .form-item {
margin-block: 0;
}
@media screen and (min-width: 600px) {
.search__form-filters {
display: grid;
......@@ -681,7 +852,7 @@
}
.search__bar-container.form-item {
margin-block-end: 1.5rem;
margin-block-end: 0.5rem;
}
.search__search-bar .search__search_term {
......@@ -690,9 +861,11 @@
border: none;
outline: none;
}
.search__filter-select {
min-width: 10rem;
}
.search__search_term::-webkit-search-cancel-button,
.search__search_term::-webkit-search-decoration,
.search__search_term::-webkit-search-results-button,
......@@ -715,18 +888,21 @@
border-color: #000;
}
.search__search-icon {
.search__search-clear {
position: absolute;
z-index: 1;
right: 10px;
bottom: 12px;
top: 0;
right: 40px;
height: 100%;
cursor: pointer;
border: none;
background: none;
}
.search__search-clear {
.search__search-submit {
position: absolute;
z-index: 1;
top: 0;
right: 40px;
right: 0;
height: 100%;
cursor: pointer;
border: none;
......@@ -749,7 +925,7 @@
border: 3px solid #f3f4f9;
}
.search__form-filters-container {
.search__form-container {
padding: 1rem 1.5rem;
background-color: #f3f4f9;
}
......@@ -805,9 +981,11 @@
background-color: #d3d4d9;
font-size: 16px;
}
.search__filter__toggle.is_open {
background-color: #adaeb3;
}
.search__filter__toggle:hover {
cursor: pointer;
}
......@@ -819,10 +997,12 @@
margin-inline-end: 4px;
padding: 0 0.25rem;
}
@media (forced-colors: active) {
.search__filter__toggle {
border: 1px solid;
}
@media (prefers-color-scheme: dark) {
.search__filter__toggle-img {
filter: invert(1);
......@@ -834,16 +1014,19 @@
.search__sort {
z-index: 2;
}
.search__sort label[for] {
cursor: pointer;
}
.search__sort label {
display: block;
font-size: 0.9rem;
font-weight: bold;
}
.search__sort-select.form-element {
border: none;
background-color: #d3d4d9;
}
.search__sort-select:hover {
cursor: pointer;
min-width: 10rem;
}
@media screen and (max-width: 855px) {
......@@ -851,14 +1034,99 @@
margin-top: 10px;
margin-bottom: 10px;
}
.search__filter-wrapper {
flex-direction: column;
align-items: flex-start;
margin: 20px 0;
}
}
@media (forced-colors: active) {
#pb-sort {
border: 1px solid;
}
}
.pb-layout__main .pb-install_bulk_actions {
margin-top: 1rem;
border-radius: 0.5rem;
}
.pb-layout__main .pb-install_bulk_actions[data-drupal-sticky-vbo="true"] {
animation: none;
}
.pb-install_bulk_actions .install_button {
padding: 5px;
color: var(--color-white);
background-color: var(--color-blue-400);
}
.pb-install_bulk_actions .install_button:hover {
background-color: var(--color-blue-500);
}
.pb-install_bulk_actions button.install_button_common {
height: 34px;
margin-left: 20px;
font-size: 0.79rem;
margin-block: 0;
}
.select_button {
padding: calc(0.5rem - 1px) calc(1rem - 1px);
cursor: pointer;
color: var(--color-blue-600);
border: 2px solid var(--color-blue-600);
border-radius: 2px;
background-color: #fff;
font-size: 0.79rem;
font-weight: bold;
}
.select_button:hover {
color: var(--color-blue-650);
border: 2px solid var(--color-blue-650);
}
.select_button:active {
color: var(--color-blue-700);
border: 2px solid var(--color-blue-700);
}
.project_status-indicator__label {
margin-left: 5px;
color: var(--color-gray-800);
font-size: 16px;
font-weight: 700;
}
.pb-project__status-icon-span {
display: flex;
}
.pb-install_bulk_actions button.clear_button {
margin-block: 0;
margin-inline: 0;
height: 34px;
margin-left: 10px;
padding: 5px;
font-size: 0.79rem;
}
[data-project-browser-instance-id] {
position: relative;
}
[data-project-browser-instance-id] .pb-projects__loading-overlay {
position: absolute;
/* No element of the Project Browser UI should have a higher z-index than this. */
z-index: 100;
top: 0;
left: 0;
display: flex;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, 0.6);
}
<svg width="31" height="31" viewBox="3 4 25 23" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M24.2438 7.82616L16.2878 4.41643C15.7847 4.20081 15.2152 4.20081 14.7121 4.41643L6.75603 7.82616C5.9368 8.17726 5.44876 9.0281 5.55932 9.91252L6.46397 17.1497C6.68412 18.9109 7.56509 20.5228 8.92862 21.659L14.2196 26.0681C14.9612 26.6862 16.0386 26.6862 16.7803 26.0681L22.0712 21.659C23.4347 20.5228 24.3157 18.9109 24.5359 17.1497L25.4405 9.91252C25.5511 9.0281 25.063 8.17726 24.2438 7.82616Z" stroke="#003CC5" stroke-width="2" stroke-linecap="round"/>
<path d="M11.6251 15.5L15.0688 18.9437C15.2919 19.1668 15.6634 19.1301 15.8384 18.8675L20.6667 11.625" stroke="#003CC5" stroke-width="2" stroke-linecap="round"/>
</svg>
\ No newline at end of file
</svg>
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 13"><path d="M2 6.571L5.6 10 14 2" fill="none" stroke="#42a877" stroke-width="3"/></svg>
<svg width="31" height="31" viewBox="-3 1 25 23" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M19.7856 4.65941L11.8295 1.24968C11.3264 1.03406 10.7569 1.03406 10.2538 1.24968L2.29778 4.65941C1.47855 5.01051 0.990512 5.86135 1.10106 6.74577L2.00572 13.983C2.22587 15.7442 3.10684 17.356 4.47037 18.4923L9.7613 22.9014C10.503 23.5195 11.5803 23.5195 12.322 22.9014L17.613 18.4923C18.9765 17.356 19.8575 15.7442 20.0776 13.983L20.9823 6.74577C21.0928 5.86135 20.6048 5.01051 19.7856 4.65941Z"
stroke="#71B43D" stroke-width="2" stroke-linecap="round"/>
<path d="M8.11154 10.9899L7.34928 11.6372L7.88422 12.2672L8.60366 11.8605L8.11154 10.9899ZM11.6207 9.00614L12.1129 9.87667L12.8539 9.45775L12.5587 8.65935L11.6207 9.00614ZM13.2092 18.0066C13.4792 18.4883 14.0887 18.6599 14.5704 18.3899C15.0522 18.1198 15.2238 17.5104 14.9537 17.0286L13.2092 18.0066ZM5.57218 7.16219C5.40773 7.64804 5.34215 8.11967 5.39734 8.58696C5.45262 9.05488 5.62146 9.45431 5.8345 9.80386C6.04064 10.1421 6.3001 10.4518 6.54986 10.7351C6.81997 11.0414 7.06271 11.2997 7.34928 11.6372L8.87379 10.3427C8.57775 9.99404 8.26679 9.65818 8.04992 9.41225C7.81271 9.14325 7.6519 8.94281 7.54229 8.76297C7.43959 8.59446 7.3972 8.468 7.38353 8.35234C7.3698 8.23605 7.37758 8.06639 7.4666 7.80341L5.57218 7.16219ZM12.5587 8.65935C12.4279 8.3056 12.3295 7.96118 12.2183 7.58989C12.1136 7.24024 11.9914 6.84446 11.8225 6.48856C11.65 6.12512 11.3978 5.73392 10.9911 5.43968C10.5709 5.13569 10.0753 4.99929 9.53106 4.99669L9.52149 6.99666C9.72727 6.99765 9.79608 7.04363 9.81883 7.06008C9.85505 7.08629 9.92429 7.15361 10.0156 7.34604C10.1105 7.54602 10.1945 7.80345 10.3024 8.16366C10.4038 8.50222 10.5243 8.92429 10.6828 9.35293L12.5587 8.65935ZM8.60366 11.8605L10.3583 10.8686L9.37402 9.12752L7.61942 10.1194L8.60366 11.8605ZM10.3583 10.8686L12.1129 9.87667L11.1286 8.13561L9.37402 9.12752L10.3583 10.8686ZM8.99385 10.487L13.2092 18.0066L14.9537 17.0286L10.7384 9.50906L8.99385 10.487Z"
fill="#71B43D"/>
<mask id="path-3-inside-1_137_4246" fill="white">
<path d="M14 16.5C14 16.7761 13.7761 17 13.5 17C13.2239 17 13 16.7761 13 16.5C13 16.2239 13.2239 16 13.5 16C13.7761 16 14 16.2239 14 16.5Z"/>
</mask>
<path d="M14 16.5C14 16.7761 13.7761 17 13.5 17C13.2239 17 13 16.7761 13 16.5C13 16.2239 13.2239 16 13.5 16C13.7761 16 14 16.2239 14 16.5Z"
fill="white"/>
<path d="M13 16.5C13 16.2239 13.2239 16 13.5 16V18C14.3284 18 15 17.3284 15 16.5H13ZM13.5 16C13.7761 16 14 16.2239 14 16.5H12C12 17.3284 12.6716 18 13.5 18V16ZM14 16.5C14 16.7761 13.7761 17 13.5 17V15C12.6716 15 12 15.6716 12 16.5H14ZM13.5 17C13.2239 17 13 16.7761 13 16.5H15C15 15.6716 14.3284 15 13.5 15V17Z"
fill="#71B43D" mask="url(#path-3-inside-1_137_4246)"/>
</svg>
<svg fill="#55565B" height="16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m6.464 13.676c-.194.194-.513.194-.707 0l-4.96-4.955c-.194-.193-.194-.513 0-.707l1.405-1.407c.194-.195.512-.195.707 0l2.849 2.848c.194.193.513.193.707 0l6.629-6.626c.195-.194.514-.194.707 0l1.404 1.404c.193.194.193.513 0 .707z"/></svg>
name: Project Browser Devel
type: module
description: Provides a Project Browser source plugin that generates random data.
core_version_requirement: ^10 || ^11
dependencies:
- project_browser:project_browser
<?php
/**
* @file
* Install and uninstall functions for the project_browser_devel module.
*/
/**
* Implements hook_install().
*/
function project_browser_devel_install() {
// Set the new random data generator as plugin and keep the current one.
$configFactory = \Drupal::configFactory();
$current_source_plugin = $configFactory->getEditable('project_browser.admin_settings')
->get('enabled_sources');
$current_source_plugin[] = 'random_data';
$configFactory->getEditable('project_browser.admin_settings')
->set('enabled_sources', $current_source_plugin)
->save(TRUE);
}
/**
* Implements hook_uninstall().
*/
function project_browser_devel_uninstall() {
// Set the previous plugin.
$admin_settings = \Drupal::configFactory()->getEditable('project_browser.admin_settings');
$enabled_sources = $admin_settings->get('enabled_sources');
if (($key = array_search('random_data', $enabled_sources)) !== FALSE) {
unset($enabled_sources[$key]);
$admin_settings
->set('enabled_sources', array_values($enabled_sources) ?: ['drupalorg_jsonapi'])
->save(TRUE);
}
}
<?php
namespace Drupal\project_browser_devel\Plugin\ProjectBrowserSource;
use Drupal\Component\Utility\Random;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\project_browser\Plugin\ProjectBrowserSourceBase;
use Drupal\project_browser\ProjectBrowser\Filter\BooleanFilter;
use Drupal\project_browser\ProjectBrowser\Filter\MultipleChoiceFilter;
use Drupal\project_browser\ProjectBrowser\Project;
use Drupal\project_browser\ProjectBrowser\ProjectsResultsPage;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Random data plugin. Used mostly for testing.
*
* To enable this source use the following drush command.
* phpcs:ignore
* drush config:set project_browser.admin_settings enabled_source random_data
*
* @ProjectBrowserSource(
* id = "random_data",
* label = @Translation("Random data"),
* description = @Translation("Gets random project and filters information"),
* )
*/
class RandomDataPlugin extends ProjectBrowserSourceBase {
/**
* Utility to create random data.
*
* @var \Drupal\Component\Utility\Random
*/
protected $randomGenerator;
/**
* ProjectBrowser cache bin.
*
* @var \Drupal\Core\Cache\CacheBackendInterface
*/
protected $cacheBin;
/**
* Constructs a MockDrupalDotOrg object.
*
* @param array $configuration
* A configuration array containing information about the plugin instance.
* @param string $plugin_id
* The plugin ID for the plugin instance.
* @param mixed $plugin_definition
* The plugin implementation definition.
* @param \Drupal\Core\Cache\CacheBackendInterface $cache_bin
* The cache bin.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, CacheBackendInterface $cache_bin) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
$this->randomGenerator = new Random();
$this->cacheBin = $cache_bin;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('cache.project_browser'),
);
}
/**
* Generate random IDs and labels.
*
* @param int $array_length
* Length of the array to generate.
*
* @return array
* Array of random IDs and names.
*/
protected function getRandomIdsAndNames($array_length = 4): array {
$data = [];
for ($i = 0; $i < $array_length; $i++) {
$data[] = [
'id' => uniqid(),
'name' => ucwords($this->randomGenerator->word(rand(6, 10))),
];
}
return $data;
}
/**
* Returns a random date.
*
* @return int
* Random timestamp.
*/
protected function getRandomDate() {
return rand(strtotime('2 years ago'), strtotime('today'));
}
/**
* {@inheritdoc}
*/
public function getCategories(): array {
$stored_categories = $this->cacheBin->get('RandomData:categories');
if ($stored_categories) {
$categories = $stored_categories->data;
}
else {
$categories = $this->getRandomIdsAndNames(20);
$this->cacheBin->set('RandomData:categories', $categories);
}
return $categories;
}
/**
* {@inheritdoc}
*/
public function getFilterDefinitions(): array {
$filters = [];
$categories = $this->getCategories();
$choices = array_combine(
array_column($categories, 'id'),
array_column($categories, 'name'),
);
$filters['categories'] = new MultipleChoiceFilter($choices, [], $this->t('Categories'), NULL);
$filters['maintained'] = new BooleanFilter(
TRUE,
$this->t('Only show actively maintained projects'),
NULL,
);
$filters['security_advisories'] = new BooleanFilter(
TRUE,
$this->t('Only show projects covered by a security policy'),
NULL,
);
$filters['developed'] = new BooleanFilter(
TRUE,
$this->t('Only show projects under active development'),
NULL,
);
return $filters;
}
/**
* {@inheritdoc}
*/
public function getProjects(array $query = []) : ProjectsResultsPage {
$projects = $this->getProjectData();
// Filter by project machine name.
if (!empty($query['machine_name'])) {
$projects = array_filter($projects, fn(Project $project) => $project->machineName === $query['machine_name']);
}
// Filter by categories.
if (!empty($query['categories'])) {
$projects = array_filter($projects, fn(Project $project) => array_intersect(array_column($project->categories, 'id'), explode(',', $query['categories'])));
}
// Filter by search text.
if (!empty($query['search'])) {
$projects = array_filter($projects, fn(Project $project) => stripos($project->title, $query['search']) !== FALSE);
}
return $this->createResultsPage($projects);
}
/**
* Gets the project data from cache if available, or builds it if not.
*/
private function getProjectData(): array {
$stored_projects = $this->cacheBin->get('RandomData:projects');
if ($stored_projects) {
return $stored_projects->data;
}
$projects = [];
$number_of_projects = rand(16, 36);
$categories = $this->getCategories();
$broken_image = 'https://image.not/found' . uniqid() . '.jpg';
$good_image = 'https://picsum.photos/600/400';
for ($i = 0; $i < $number_of_projects; $i++) {
$machine_name = strtolower($this->randomGenerator->word(10));
$project_images = [];
if ($i !== 0) {
$project_images[] = [
'file' => [
'uri' => str_replace(4, 5, $good_image),
'resource' => 'image',
],
'alt' => $machine_name . ' something',
];
$project_images[] = [
'file' => [
'uri' => str_replace(4, 6, $good_image),
'resource' => 'image',
],
'alt' => $machine_name . ' another thing',
];
}
$projects[] = new Project(
logo: [
'file' => [
'uri' => ($i % 3) ? $good_image : $broken_image,
'resource' => 'image',
],
'alt' => $machine_name . ' logo',
],
isCompatible: (bool) ($i / 4),
isMaintained: (bool) rand(0, 1),
isCovered: (bool) rand(0, 1),
isActive: (bool) rand(0, 1),
starUserCount: rand(0, 100),
projectUsageTotal: rand(0, 100000),
machineName: $machine_name,
body: [
'summary' => $this->randomGenerator->paragraphs(1),
'value' => $this->randomGenerator->paragraphs(5),
],
title: ucwords($machine_name),
changed: $this->getRandomDate(),
created: $this->getRandomDate(),
author: [
'name' => $this->randomGenerator->word(10),
],
packageName: 'random/' . $machine_name,
categories: [$categories[array_rand($categories)]],
images: $project_images,
id: $machine_name,
);
}
$this->cacheBin->set('RandomData:projects', $projects);
return $projects;
}
}
......@@ -10,7 +10,7 @@
/**
* Implements hook_install().
*/
function project_browser_source_example_install() {
function project_browser_source_example_install(): void {
$configFactory = \Drupal::configFactory();
$current_source_plugin = $configFactory->getEditable('project_browser.admin_settings')
->get('enabled_sources');
......@@ -23,7 +23,7 @@ function project_browser_source_example_install() {
/**
* Implements hook_uninstall().
*/
function project_browser_source_example_uninstall() {
function project_browser_source_example_uninstall(): void {
$admin_settings = \Drupal::configFactory()->getEditable('project_browser.admin_settings');
$enabled_sources = $admin_settings->get('enabled_sources');
if (($key = array_search('project_browser_source_example', $enabled_sources)) !== FALSE) {
......
......@@ -2,8 +2,12 @@
namespace Drupal\project_browser_source_example\Plugin\ProjectBrowserSource;
use Drupal\Core\Extension\ModuleExtensionList;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\Core\Url;
use Drupal\project_browser\Attribute\ProjectBrowserSource;
use Drupal\project_browser\Plugin\ProjectBrowserSourceBase;
use Drupal\project_browser\ProjectBrowser\Filter\MultipleChoiceFilter;
use Drupal\project_browser\ProjectBrowser\Filter\TextFilter;
use Drupal\project_browser\ProjectBrowser\Project;
use Drupal\project_browser\ProjectBrowser\ProjectsResultsPage;
use Symfony\Component\DependencyInjection\ContainerInterface;
......@@ -11,14 +15,13 @@ use Symfony\Component\HttpFoundation\RequestStack;
/**
* Project Browser Source Plugin example code.
*
* @ProjectBrowserSource(
* id = "project_browser_source_example",
* label = @Translation("Example source"),
* description = @Translation("Example source plugin for Project Browser."),
* )
*/
class ProjectBrowserSourceExample extends ProjectBrowserSourceBase {
#[ProjectBrowserSource(
id: 'project_browser_source_example',
label: new TranslatableMarkup('Example source'),
description: new TranslatableMarkup('Example source plugin for Project Browser.'),
)]
final class ProjectBrowserSourceExample extends ProjectBrowserSourceBase {
/**
* Constructor for example plugin.
......@@ -31,15 +34,12 @@ class ProjectBrowserSourceExample extends ProjectBrowserSourceBase {
* The plugin implementation definition.
* @param \Symfony\Component\HttpFoundation\RequestStack $requestStack
* The request from the browser.
* @param \Drupal\Core\Extension\ModuleExtensionList $moduleExtensionList
* The module extension list.
*/
public function __construct(
array $configuration,
$plugin_id,
$plugin_definition,
protected readonly RequestStack $requestStack,
protected ModuleExtensionList $moduleExtensionList,
) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
}
......@@ -47,13 +47,12 @@ class ProjectBrowserSourceExample extends ProjectBrowserSourceBase {
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get(RequestStack::class),
$container->get(ModuleExtensionList::class),
);
}
......@@ -84,8 +83,7 @@ class ProjectBrowserSourceExample extends ProjectBrowserSourceBase {
'label' => 'Project 1',
'short_description' => 'Quick summary to show in the cards.',
'long_description' => 'Extended project information to show in the detail page',
'author' => 'Jane Doe',
'logo' => $request->getSchemeAndHttpHost() . '/core/misc/logo/drupal-logo.svg',
'logo' => $request?->getSchemeAndHttpHost() . '/core/misc/logo/drupal-logo.svg',
'created_at' => strtotime('1 year ago'),
'updated_at' => strtotime('1 month ago'),
'categories' => ['cat_1:Category 1'],
......@@ -108,20 +106,7 @@ class ProjectBrowserSourceExample extends ProjectBrowserSourceBase {
// fully populated.
$projects = [];
foreach ($projects_from_source as $project_from_source) {
// Empty array if no author information is provided.
$author = [
'name' => $project_from_source['author'],
];
// Empty array if there is no logo.
$logo = [
'file' => [
// Url of the logo in "https" format.
'uri' => $project_from_source['logo'],
'resource' => 'image',
],
'alt' => 'Project 1 logo',
];
$logo = Url::fromUri($project_from_source['logo']);
// Empty array if there are no categories.
$categories = [];
......@@ -140,24 +125,19 @@ class ProjectBrowserSourceExample extends ProjectBrowserSourceBase {
isCompatible: TRUE,
isMaintained: TRUE,
isCovered: TRUE,
isActive: TRUE,
starUserCount: 0,
projectUsageTotal: 0,
machineName: $project_from_source['unique_name'],
body: [
'summary' => $project_from_source['short_description'],
'value' => $project_from_source['long_description'],
],
title: $project_from_source['label'],
changed: $project_from_source['updated_at'],
created: $project_from_source['created_at'],
author: $author,
packageName: $project_from_source['composer_namespace'],
categories: $categories,
// Images: Array of images using the same structure as $logo, above.
// Images: Array of images, each of which is an array with two elements:
// `file`, which is a \Drupal\Core\Url object pointing to the image,
// and `alt`, which is the alt text.
images: [],
);
$pb_path = $this->moduleExtensionList->getPath('project_browser');
$projects[] = new Project(
logo: $logo,
// Maybe the source won't have all fields, but we still need to
......@@ -165,21 +145,17 @@ class ProjectBrowserSourceExample extends ProjectBrowserSourceBase {
isCompatible: TRUE,
isMaintained: TRUE,
isCovered: TRUE,
isActive: TRUE,
starUserCount: 0,
projectUsageTotal: 0,
machineName: $project_from_source['unique_name'] . '2',
body: [
'summary' => $project_from_source['short_description'] . ' (different commands)',
'value' => $project_from_source['long_description'] . ' (different commands)',
],
title: 'A project with different commands',
changed: $project_from_source['updated_at'],
created: $project_from_source['created_at'],
author: $author,
packageName: $project_from_source['composer_namespace'],
categories: $categories,
// Images: Array of images using the same structure as $logo, above.
// Images: Array of images, each of which is an array with two elements:
// `file`, which is a \Drupal\Core\Url object pointing to the image,
// and `alt`, which is the alt text.
images: [],
);
}
......@@ -192,21 +168,18 @@ class ProjectBrowserSourceExample extends ProjectBrowserSourceBase {
/**
* {@inheritdoc}
*/
public function getCategories(): array {
// Step 1: The list of categories that the modules can have. The array
// must have the "id" and "name" properties defined. If your source data
// does not use categories then return an empty array. These can be
// hardcoded, come from REST or GraphQL endpoints, etc.
return [
[
'id' => 'cat_1',
'name' => 'Category 1',
],
[
'id' => 'cat_2',
'name' => 'Category 2',
],
public function getFilterDefinitions(): array {
$filters = [
'search' => new TextFilter('', $this->t('Search')),
];
$categories = [
'cat_1' => 'Category 1',
'cat_2' => 'Category 2',
];
$filters['categories'] = new MultipleChoiceFilter($categories, [], $this->t('Categories'));
return $filters;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<ruleset name="project_browser">
<!-- Drupal (core) uses stylelint: https://www.drupal.org/list-changes/drupal/published?keywords_description=stylelint-->
<exclude-pattern>*.css</exclude-pattern>
<!-- Drupal (core) uses eslint: https://www.drupal.org/list-changes/drupal/published?keywords_description=eslint-->
<exclude-pattern>*.js</exclude-pattern>
<!-- Use the Drupal core coding standard -->
<rule ref="./core.phpcs.xml.dist"></rule>
</ruleset>