From afbe3848e4212f8f5f9250fc0f6bd392db375344 Mon Sep 17 00:00:00 2001 From: Amol Wankhede Date: Thu, 2 May 2024 10:02:43 +0530 Subject: [PATCH] Show in search check for files (#92) * Index or remove from index based on ShowInSearch value * Add Show in search and last indexed field to file details tab * Add tests and revert onAfterPublish code * Add DataObject search content controls * Generic SearchFormFactoryExtension and fixing tests * Add config and update tests * Default config and update doc * Update ShowInSearch if files is not set to index * Add tests for SearchFormFactoryExtension * Fix broken tests * Linting fixes * Show in search remove duplicated field for SiteTree main tab * Update settings for SiteTree objects --- _config/extensions.yml | 9 +++ docs/en/configuration.md | 23 ++++++ src/Extensions/SearchFormFactoryExtension.php | 63 ++++++++++++++++ src/Extensions/SearchServiceExtension.php | 49 +++++++++++-- tests/DataObject/DataObjectDocumentTest.php | 28 ++++++++ .../SearchFormFactoryExtensionTest.php | 68 ++++++++++++++++++ tests/Fake/DataObjectFake.php | 1 - tests/fixtures.yml | 13 ++++ tests/silverstripe-logo.png | Bin 0 -> 2230 bytes tests/test-file.pdf | Bin 0 -> 5305 bytes 10 files changed, 247 insertions(+), 7 deletions(-) create mode 100644 src/Extensions/SearchFormFactoryExtension.php create mode 100644 tests/Extensions/SearchFormFactoryExtensionTest.php create mode 100644 tests/silverstripe-logo.png create mode 100644 tests/test-file.pdf diff --git a/_config/extensions.yml b/_config/extensions.yml index a453b3e..ff399e8 100644 --- a/_config/extensions.yml +++ b/_config/extensions.yml @@ -28,3 +28,12 @@ SilverStripe\CMS\Model\SiteTree: extensions: SearchServiceExtension: SilverStripe\SearchService\Extensions\SearchServiceExtension SiteTreeHierarchyExtension: SilverStripe\SearchService\Extensions\SiteTreeHierarchyExtension +--- +Name: search-service-form-extension +--- +SilverStripe\AssetAdmin\Forms\FileFormFactory: + extensions: + - SilverStripe\SearchService\Extensions\SearchFormFactoryExtension +SilverStripe\SearchService\Extensions\SearchFormFactoryExtension: + exclude_classes: + - SilverStripe\Assets\Image diff --git a/docs/en/configuration.md b/docs/en/configuration.md index 9b1f0a0..c3a67ee 100644 --- a/docs/en/configuration.md +++ b/docs/en/configuration.md @@ -254,6 +254,29 @@ __Additional note__: This is handled via `SubsiteIndexConfigurationExtension` - this logic could be replicated for other scenarios like languages if required. +## Configuring search exclusion for files + +By default, `SilverStripe\Assets\Image` is excluded from the search. To change this default +setting, use the code snippet below. + +```yaml +--- +After: search-service-form-extension +--- +SilverStripe\SearchService\Extensions\SearchFormFactoryExtension: + exclude_classes: null +``` + +If you want to exclude certain file extensions from being added to the search index, add +the following configuration to your code base: + +```yaml +SilverStripe\SearchService\Extensions\SearchFormFactoryExtension: + exclude_file_extensions: + - svg + - mp4 +``` + ## More information * [Usage](usage.md) diff --git a/src/Extensions/SearchFormFactoryExtension.php b/src/Extensions/SearchFormFactoryExtension.php new file mode 100644 index 0000000..32e9913 --- /dev/null +++ b/src/Extensions/SearchFormFactoryExtension.php @@ -0,0 +1,63 @@ +Fields()->findOrMakeTab('Editor.Details'); + $file = $context['Record'] ?? null; + + $excludedClasses = Config::inst()->get(self::class, 'exclude_classes') ?? []; + $excludeFileTypes = Config::inst()->get(self::class, 'exclude_file_extensions') ?? []; + + if (!$fields || !$file) { + return; + } + + if (in_array($file->ClassName, $excludedClasses) || in_array($file->getExtension(), $excludeFileTypes)) { + if ($file->ShowInSearch) { + $file->ShowInSearch = false; + $file->write(); + } + + return; + } + + $fields->push( + CheckboxField::create( + 'ShowInSearch', + _t( + 'SilverStripe\\AssetAdmin\\Controller\\AssetAdmin.SHOWINSEARRCH', + 'Show in search?' + ) + ) + ); + + $fields->push( + DatetimeField::create( + 'SearchIndexed', + _t( + 'SilverStripe\\SearchService\\Extensions\\SearchServiceExtension.LastIndexed', + 'Last indexed in search' + ) + ) + ->setReadonly(true) + ); + } + +} diff --git a/src/Extensions/SearchServiceExtension.php b/src/Extensions/SearchServiceExtension.php index e7d148c..06291c8 100644 --- a/src/Extensions/SearchServiceExtension.php +++ b/src/Extensions/SearchServiceExtension.php @@ -3,8 +3,10 @@ namespace SilverStripe\SearchService\Extensions; use Exception; +use SilverStripe\CMS\Model\SiteTree; use SilverStripe\Core\Config\Configurable; use SilverStripe\Core\Injector\Injectable; +use SilverStripe\Forms\CheckboxField; use SilverStripe\Forms\FieldList; use SilverStripe\Forms\ReadonlyField; use SilverStripe\ORM\DataExtension; @@ -36,9 +38,14 @@ class SearchServiceExtension extends DataExtension use BatchProcessorAware; private static array $db = [ + 'ShowInSearch' => 'Boolean', 'SearchIndexed' => 'Datetime', ]; + private static array $defaults = [ + 'ShowInSearch' => true, + ]; + private bool $hasConfigured = false; public function __construct( @@ -53,19 +60,49 @@ public function __construct( $this->setBatchProcessor($batchProcessor); } + /** + * General DataObject Search settings + * + * @param FieldList $fields + * @return void + */ public function updateCMSFields(FieldList $fields): void { - if (!$this->getConfiguration()->isEnabled()) { + if ($this->owner instanceof SiteTree || !$this->getConfiguration()->isEnabled()) { return; } - $field = ReadonlyField::create('SearchIndexed', _t(self::class.'.LastIndexed', 'Last indexed in search')); + $showInSearchField = CheckboxField::create( + 'ShowInSearch', + _t(self::class . '.ShowInSearch', 'Show in search?') + ); + $searchIndexedField = ReadonlyField::create( + 'SearchIndexed', + _t(self::class . '.LastIndexed', 'Last indexed in search') + ); + + $fields->push($showInSearchField); + $fields->push($searchIndexedField); + } - if ($fields->hasTabSet()) { - $fields->addFieldToTab('Root.Main', $field); - } else { - $fields->push($field); + /** + * Specific settings for SiteTree + * + * @param FieldList $fields + * @return void + */ + public function updateSettingsFields(FieldList $fields): void + { + if (!$this->owner instanceof SiteTree || !$this->getConfiguration()->isEnabled()) { + return; } + + $searchIndexedField = ReadonlyField::create( + 'SearchIndexed', + _t(self::class . '.LastIndexed', 'Last indexed in search') + ); + + $fields->insertAfter('ShowInSearch', $searchIndexedField); } /** diff --git a/tests/DataObject/DataObjectDocumentTest.php b/tests/DataObject/DataObjectDocumentTest.php index e2b87b7..1419017 100644 --- a/tests/DataObject/DataObjectDocumentTest.php +++ b/tests/DataObject/DataObjectDocumentTest.php @@ -758,4 +758,32 @@ public function testIndexDataObjectDocument(): void }); } + public function testIndexDataObjectDocumentShowInSearch(): void + { + $dataObject = $this->objFromFixture(DataObjectFakeVersioned::class, 'two'); + $doc = DataObjectDocument::create($dataObject); + + $config = $this->mockConfig(); + $config->set( + 'getIndexesForDocument', + [ + $doc->getIdentifier() => [ + 'index' => 'data', + ], + ] + ); + + // Should not index as ShowInSearch is false for this DataObject + $dataObject->publishRecursive(); + $this->assertFalse($doc->shouldIndex()); + + // Should index as ShowInSearch is now set to true + $dataObject->ShowInSearch = true; + $dataObject->publishRecursive(); + + $doc = DataObjectDocument::create($dataObject); + + $this->assertTrue($doc->shouldIndex()); + } + } diff --git a/tests/Extensions/SearchFormFactoryExtensionTest.php b/tests/Extensions/SearchFormFactoryExtensionTest.php new file mode 100644 index 0000000..ffd1288 --- /dev/null +++ b/tests/Extensions/SearchFormFactoryExtensionTest.php @@ -0,0 +1,68 @@ +get(SearchFormFactoryExtension::class, 'exclude_classes'); + $this->assertEquals($expected, $actual); + } + + public function testImageAndFileInclusionInShowInSearch(): void + { + $form = Form::create(); + $fields = new FieldList(new TabSet('Editor')); + $form->setFields($fields); + + $image = $this->objFromFixture(Image::class, 'image'); + // Every file has default ShowInSearch value of 1 + // (https://github.com/silverstripe/silverstripe-assets/blob/2/src/File.php#L163) + $this->assertEquals(1, $image->ShowInSearch); + + $searchFormFactoryExtension = new SearchFormFactoryExtension(); + $searchFormFactoryExtension->updateForm($form, null, 'Form', ['Record' => $image]); + // By default, `SilverStripe\Assets\Image` is excluded from the search - see `_config/extensions.yml` + $this->assertEquals(0, $image->ShowInSearch); + + $file = $this->objFromFixture(File::class, 'pdf-file'); + $searchFormFactoryExtension->updateForm($form, null, 'Form', ['Record' => $file]); + $this->assertEquals(1, $file->ShowInSearch); + } + + public function testExcludedFileExtensionShowInSearch(): void + { + // Modify config to exclude pdf files from search + Config::modify()->set(SearchFormFactoryExtension::class, 'exclude_file_extensions', ['pdf']); + + $file = $this->objFromFixture(File::class, 'pdf-file'); + // Default ShowInSearch value of 1 + $this->assertEquals(1, $file->ShowInSearch); + + $form = Form::create(); + $fields = new FieldList(new TabSet('Editor')); + $form->setFields($fields); + + $searchFormFactoryExtension = new SearchFormFactoryExtension(); + $searchFormFactoryExtension->updateForm($form, null, 'Form', ['Record' => $file]); + $this->assertEquals(0, $file->ShowInSearch); + } + +} diff --git a/tests/Fake/DataObjectFake.php b/tests/Fake/DataObjectFake.php index ea6164c..333231e 100644 --- a/tests/Fake/DataObjectFake.php +++ b/tests/Fake/DataObjectFake.php @@ -19,7 +19,6 @@ class DataObjectFake extends DataObject implements TestOnly private static array $db = [ 'Title' => 'Varchar', - 'ShowInSearch' => 'Boolean', 'Sort' => 'Int', ]; diff --git a/tests/fixtures.yml b/tests/fixtures.yml index 4ee023f..5356b8f 100644 --- a/tests/fixtures.yml +++ b/tests/fixtures.yml @@ -64,3 +64,16 @@ SilverStripe\Subsites\Model\Subsite: Title: 'Subsite 1' subsite2: Title: 'Subsite 2' + + +SilverStripe\Assets\Image: + image: + Name: 'Image' + Filename: silverstripe-logo.png + PopulateFileFrom: tests/silverstripe-logo.png + +SilverStripe\Assets\File: + pdf-file: + Title: Test File + Filename: test-file.pdf + PopulateFileFrom: tests/test-file.pdf diff --git a/tests/silverstripe-logo.png b/tests/silverstripe-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..c70ca447f78f0fd524f4db1f57ada9f9b448a6fc GIT binary patch literal 2230 zcmZ`)`#Tei7he%YqDkb{mq_IvUs)I`bD6}XmZ3Ts=uWTIzWvn#xvbrcc+Wzy| zwOgj)i!Y1UkuT|qWgC{*MMCu^BzOUYo|k<*8~JVxLt2GLE{eF!cqJ`U7Yxq=0KC&s z$Ze~j%(bk?p$vJUX#Phsu_GQ6sU@GveAYOez^i39PH`I>#JR2G8xXF?PYYJ%si+*C z^k31)i3uSyPcqeB1{C13-;RP?En1k@m4T zn`<*~F()_f;SIPp%DEk5*#$Viz(a%GcHMJ5rufC)uU=gK-Bo)7_%zh2zQ0R|@lM`U zrx~@W+n;1y_CcEPSwu2V(dH+zDo8jcY^)Pa``1Bo!+?W zl%q208=|C2*a)=`7es(`22F-dlAg8cet#TNlHY63`5V3-_lemtlu6a8bL$cV=;#hH?cxZ9Uv4r({dX7!*dQASJ)Bb_akjw|e zmbaCCeq#kjh}UNmKML0K&yTkhQ8z$6R81}T*w|ew-QQjsX)_&xjz94o0b9Q`iX3pj zcFFiuhFhqTofPQl(v6|l7lF|$$(LA!U1xSt^JF<^?Zuf{eAz zPA=aSWvgqbC1egu>Z#GF@!h=o+dh?&BS~>{5?58jGo=VAW>VYpRYt;{-5m+Y-`PXo zABI4RuC3I>Vd!d;dJA1(o93yGwl$d|aj}SDzYkw;;URV9sY(x` z6>Zq4LlYHVZNg`#x5934GF$19`7U@0hIbN0tzKv)jYClo1R{Seg(rH*bS{dN;m5Oo`T)h=dGnl1K8GhZobA4~yy%xF{D2`A$hxrG`E70! zDGXDX*on;@%uH|w@HSuR-n z+fob*b3Wy+ng>UG5qDC7OJB6HDQJ6npZmuOgk%9WF?)a_*?m5^CE;V_4n9>YY7N?u z(G6=WPaN_Q@D39Ro)0bJv1b@xE9@%$l9TilozXXYw4OkwgRNG;UF&{gEppR_6d~(m zfeU}|)gB{M;kN$-xt99_Z1umGzbePE{u-$jBsx9RGXGNs*=E*jX{%;gQ#2s1%sKE< z$4vY$9uM%zVD{kwc~~aTRQ{nv*q^7cn;);Mpc)%3SngbPnOHe}L7X2tZ>$kGA66)# zm;3CT$qlgz9~G@E(`8h+Y82>DCn47Nsdw8GJ2dD>mjEAa?_v+Z!!-;QF?n~5)$WZ9hO#$ATimOwGI2EoYMUI!^N`{R)UDWt zRc@WYdD4MIXDMa92!szX*u%xe^bUAr!JDV1mUOC*{7rM-2-B< z+5V|bg+x`f=-{W1uj9Xj2-II8Pzb98-__P~?Y+vqbciU(SKyiE&&28Xl0nR{=Pi`2 z$AI+>DzE2Eu+p8i*Fv8sugq+32Yh?%ra1mX2SoL?jQ}0ZI!8rbT;<*{s>|;8 zWTc8gzakd_0W4dO723UZ5y=h(D4OF+s4U+Nf^jcQ4bKB5i16}G$z0iy9ceQ($1X*! zuwLI(eRC=bLV<+1=O-AWIo3r%T!Z~HflIN%uWs6cJuZBeCuGp8ED2dq$VQHn2!4#a zUU}aw*f53D9erNKFSHr%is5>fy@%R&U{jCvywAF6M#}Hf;^J6~6<5|Urikxaeyhg2 z6j$~(72o{82+z}l3VdXfz@87w2w1Oda+j9*XLl)Mq^iTkZm0`3x7(oBn=TVlrq zS6k~ILy!OzpgX%m)YSoHV-k(basyBxWC1AaQK&2u1N^5FSR_1&NOvIt8X6ELi$NlI zLj1Y&&znGMwAM#_P8(G8xggh`)YR0OUZA+%KlHlbH669j>pUpBc>J}7LD@~!;g^I^ zss>s)UXBB)o3?%;U-81RpN7euYGC7sKe8L2g*w46%AACyJGf-;RjF{drHVEoVIJ=0 zR<)-5&-py4@den7sXS$437+{PsAYJJQhkEF*nuFMx!t1WSJq#ieHQwrI&9H745c2t z_u}${W4-y43L&l@5E9Mh`+mVY&M`S>DuAQYSl~Dl;Ajw3mkG&*LeQc619nglK*QhwMg{Hw0X6o09nO0{ zG;PM96D>(Bzz$ptuLmewk^EVJ1E7qfQ|Sy#F9MMSa14TD!T`+5^cotVA;1+^U_F=; z6u@$DaC(kSzOSJ2(+b~h3FB-A+?^?S4rOhQL1~~h0cAYNmqH|2=E(J zJXtP@UBh16c8UK{>%-kva)K5-?X&wVZ*vC&u|Gil z^_xGGhf>v1?}HJJ{T zxjJZG_xb1+(MjHFvca~o7}-BeDfk#}CpIc)e3Y;G)v+G`bC4ka_(O9NcXwA#^yf8Y zS~^^jO@7&#Rr+pB4ii5~Zc5W&94P!Od54U38V*gG+zP=m8gCD(OXQsCc==}Pg3IG0 zR(oVF%*3mYh_53V_lGF??9?vEJfM;^eks|Zw?_j3t3&*eXM+$;o$6nFthk%hcf6z5 zXa8~v+4DSkO9UUcD0&i&+}#$vMQY%Iu8?G)@m^!w%ww(YHMK*>5;uzsJ8TxOvKdGd zKhY8(R&MuLGr~rixnY<;dC7G7y7IW-SBGyi!JgNu2a}jB$&YhAj!7PIz4)TOTsszC zou_b`UeqlVsOUmT9TfjGlsFqZFb1Cs_I!1tL3+?V7}b%K@l74sX15+&#J*VshZ- z?OacHiT=1-yu;gSuOyxk%0XR`=0XX~+;qc23 zTKJoOnEw}{a5%yT@yn-p=_gKG>Zsxmdxkn>+m%zyP%q}f$ck3ZkM8!tML3Fq22oBEyKi{v8J{% zJ;`l{iO#Jr7tVdi$h7Suo)}qdSW^gvovREF8` zV-v0narOznz?SN*sgrBC%LTX+Lk>+pm?+rSA0nwWquLnWhu!wYxREcnXP2E6IltG>Zuo6*T;VrqO}~rx z)5B6DhqU$uF@$s`&5O*PvmVrJ>gj@6sU$j89ut~t8`Dyk(miMWYJXd+exI|_a12xY zYpArryRBn8vI@0|c@#=bMhx5(6-4ai%jKS&?0ji|P^t4Gi8wAywx+1Wnl+*hL5;|%+6E_3g~C4F_parSXfe8UxFYD0!Y z3kBzVfvEGsZH{LkTkc@*n(yQb%G^KQwEt{L?^(xX%Q$BTAh-@OdehqeRLAw!*>`uB z+U}RnQ(Ir`fuU0l>}(ssL}=#DLQ@QO54vYxD%O_dEkk##Tj(NWIrK#acJjd6ll@E6 zho8$31J*8NzPGu4dA!Y;rrYkM+_Pob=Ka(4C9j<9g&q^VHw*Bt$-aN1T~wuYP_V&ua+tDn=c%lxJE z#kK%cib(E`o|5Ek{=w@h*>sQPj_6xsO~naS0txuxoQ+2xryq1Cow?T>EP5HfJL>u? zs=-B_S8iyudHwr8WJ|6*D=Q!y74)CY1DP^!{9IA>E7 zC(@%)%agWO-Om3kTlzgu2qr57F3OT{?p-d1W-y+^I+r`_iprIqr z>@c7{N{cga6ACCq%rL z+wk(5rZ4-Gi`4WsK9kV0{8B#&OSZQD7~Z9xi?-Xp?}fW*;edUhX}%m$vDYW=P351K zv5rQfrKL70m5o-GcZ#VB5BU@E#G^*mQ<&+gFS$<&s{9Ja@_RNc(lHv@@e90#q*t>K zxcj(oX};M;4&Q4tR9K)cP&t~>c2CRX{kj(~#>B`{uzN8PA^6VRLFE|zIgV$b~-w}U56F8@lN2^gVlyU zuoF34^LMgZ*!QNEEHhyR+o`Ru3)|B2vRiwgVSVnZ^4M@Oa(nE%4_}k`Er+=>yQkiY zeC(cz*OPH%%!p5meQr6?-_+l<{KmTajJ@f$iGCQvz-Nk1Uml(Nq_!AYF#AwGR~*N~ zQuUlwGkJ!2=nyFLWjJYTY@Rq`-^AK#ik5C$nnLTLg}0ylxMMEOoSNT3bhWN6rDjh> z2$oBHoU$9pyC;xVJ6k#i&lP_EU91+K{L zF+cB~;WYkswx?%TW7Wcfb3>$0gH4O$r@e7J4=_d@Bd*4gUg3Lq%!SL`q1(m{KjxfV zLy_I_Tu!5hObn=XA?|vVf#~4tfhObWi{fk zA_XG)!coFWr;^S$?O19>M`OdVr^}_Px4#W^-s~+=)Ac7w*bk_&%O6DJpIVEIBhQ+L z7Yb(O^Dh%V?~U@vtYy~_944SB)%6-tv?N?r$HSm)MRn%=PPTbgO7Q%ASo zwV1yA_H5#WN}1jfpV+p6Jh-i=?~Mj3Y)eey+SKZrtDSBeDzCy{&q*4~J$LS=NWoGu z0vjLjF7mqfeG|`jv%MicFPG1sTjm_J>slw;ZQMrRb4{1xxZ92AktrofUlY)`JSHI{ z`~f?|EE*4>wXdFnoHOw%x8d!ns1eOb&e4yaTrUwC-}DaV7Z8h2uvP87Eo!pJEM@gydZLGfbI!HQj* zN#s@k5a-OHvr@h)!c>reA_9s5 zU}!A(1H~f1%9Jy1O7H|rM{5gxTeGA4v>6lvRZ)jZAbNnwEtp+Wh}twVl>`B&wPcYz zt-*{^*@WPKgyI7B6ksch+kD>%3XWQx5$rLL;J`}uY4LLc`=d;^0}u#~68`&zMsxD) z|9XMDf&v%}r)d6nY0m!s%E>umzvkqea{AT5f71(RLCG)LHl? z-KVgqBtRZdC;E7TP5@H?-NA?D2D*j3Hi1E>0XPDKN}NJH7##h(90Ci5=dbU=5Gr8n=T{7gPyucKD+Wbke_I2F!2I?u48?JyU*CnH(7$0w zC=B%DU*(Wc1XzCl1A}wI=Lg1O5WtvZtn>pdDS;$Vr+~5roz9^XFe*UjHKe)H!SZwU z9uF3v>Lh1yUbr*T1xiGqK)P@t!LTkU7zT?_A&^{MQ5ul{EwWm`GFhO%tQZqi1{NYK Jt9wWf@*iNao@W36 literal 0 HcmV?d00001