├── .github ├── FUNDING.yml └── workflows │ ├── build.yml │ └── release.yml ├── .gitignore ├── MemberListView.Release.sln ├── MemberListView.sln ├── MemberListView ├── App_Plugins │ └── MemberListView │ │ ├── css │ │ ├── memberListView.css │ │ └── memberListView.min.css │ │ ├── dashboard │ │ ├── memberManager.controller.js │ │ └── memberManager.html │ │ ├── dialogs │ │ └── member │ │ │ ├── export.controller.js │ │ │ ├── export.html │ │ │ ├── filter.controller.js │ │ │ └── filter.html │ │ ├── lang │ │ ├── cs.xml │ │ ├── da.xml │ │ ├── de.xml │ │ ├── en-gb.xml │ │ ├── en-us.xml │ │ ├── es.xml │ │ ├── fr.xml │ │ ├── he.xml │ │ ├── it.xml │ │ ├── ja.xml │ │ ├── ko.xml │ │ ├── nb.xml │ │ ├── nl.xml │ │ ├── pl.xml │ │ ├── pt.xml │ │ ├── ru.xml │ │ ├── sv.xml │ │ ├── tr.xml │ │ ├── zh-tw.xml │ │ └── zh.xml │ │ ├── layouts │ │ └── list │ │ │ ├── list.html │ │ │ └── list.listviewlayout.controller.js │ │ ├── overlays │ │ ├── action.controller.js │ │ └── action.html │ │ ├── package.manifest │ │ ├── resources │ │ └── member.resource.js │ │ └── scss │ │ └── memberListView.scss ├── Composing │ ├── MemberListViewComposer.cs │ ├── PackageManifestComposer.cs │ └── ServerVariablesParsingHandler.cs ├── Config │ ├── MemberListView.cs │ └── memberListView.json ├── Constants.cs ├── Controllers │ └── ExtendedMemberController.cs ├── Extensions │ ├── ExportExtension.cs │ ├── HttpRequestMessageExtensions.cs │ └── SearchExtensions.cs ├── Indexing │ ├── ConfigureMemberIndex.cs │ └── MemberIndexingComponent.cs ├── MemberExamineIndexFieldNames.cs ├── MemberListView.csproj ├── Models │ ├── ExportFormat.cs │ ├── Mapping │ │ └── MemberListItemMapDefinition.cs │ ├── MemberColumn.cs │ └── MemberListItem.cs ├── Services │ ├── IMemberExtendedService.cs │ └── MemberExtendedService.cs ├── Utility │ └── MemberDataUdiParser.cs ├── build │ └── MemberListView.targets ├── compilerconfig.json └── compilerconfig.json.defaults ├── README.md ├── README.nuget.md ├── WebSample.UmbacoV10 └── .gitignore ├── WebSample.V11 ├── .gitignore ├── Program.cs ├── Properties │ └── launchSettings.json ├── Startup.cs ├── Views │ ├── Partials │ │ ├── blockgrid │ │ │ ├── area.cshtml │ │ │ ├── areas.cshtml │ │ │ ├── default.cshtml │ │ │ └── items.cshtml │ │ ├── blocklist │ │ │ └── default.cshtml │ │ └── grid │ │ │ ├── bootstrap3-fluid.cshtml │ │ │ ├── bootstrap3.cshtml │ │ │ └── editors │ │ │ ├── base.cshtml │ │ │ ├── embed.cshtml │ │ │ ├── macro.cshtml │ │ │ ├── media.cshtml │ │ │ ├── rte.cshtml │ │ │ └── textstring.cshtml │ └── _ViewImports.cshtml ├── WebSample.V11.csproj ├── appsettings.Development.json ├── appsettings.json ├── umbraco │ └── Data │ │ └── Umbraco.sqlite.db └── wwwroot │ └── favicon.ico ├── WebSampleV12 ├── Program.cs ├── Properties │ └── launchSettings.json ├── Startup.cs ├── Views │ └── _ViewImports.cshtml ├── WebSampleV12.csproj ├── appsettings-schema.Umbraco.Cms.json ├── appsettings-schema.json ├── appsettings.Development.json ├── appsettings.json ├── umbraco │ └── Data │ │ └── Umbraco.sqlite.db └── wwwroot │ └── favicon.ico ├── assets └── Membership_logo.png ├── package.xml └── umbraco-marketplace.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: YourITGroup 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: MemberListView - CI 2 | 3 | on: 4 | pull_request: 5 | 6 | env: 7 | SOLUTION: MemberListView.Release.sln 8 | OUTPUT_PATH: ${{ github.workspace }}/.output 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | matrix: 16 | dotnet-version: ['7.x' ] 17 | 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v2 21 | 22 | - name: Fetch all history for all tags and branches 23 | run: git fetch --prune --unshallow 24 | 25 | - name: Setup .NET Core SDK ${{ matrix.dotnet-version }} 26 | uses: actions/setup-dotnet@v3 27 | 28 | with: 29 | dotnet-version: ${{ matrix.dotnet-version }} 30 | 31 | - name: Install dependencies 32 | run: dotnet restore ${{ env.SOLUTION }} 33 | 34 | - name: Build 35 | run: dotnet build ${{ env.SOLUTION }} --no-restore --configuration Release 36 | 37 | - name: Create the package 38 | run: dotnet pack ${{ env.SOLUTION }} --no-build --no-restore --configuration Release --property:PackageOutputPath=${{ env.OUTPUT_PATH }} 39 | # https://learn.microsoft.com/en-us/dotnet/core/compatibility/sdk/7.0/solution-level-output-no-longer-valid 40 | 41 | - name: Upload NuGet artifacts 42 | uses: actions/upload-artifact@v1 43 | with: 44 | name: nuget 45 | path: ${{ env.OUTPUT_PATH }} 46 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: MemberListView - Release 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | 7 | env: 8 | SOLUTION: MemberListView.Release.sln 9 | PLUGIN_PROJECT: MemberListView/MemberListView.csproj 10 | OUTPUT_PATH: ${{ github.workspace }}/.output 11 | 12 | jobs: 13 | publish-nuget: 14 | runs-on: ubuntu-latest 15 | permissions: 16 | contents: write 17 | 18 | strategy: 19 | matrix: 20 | dotnet-version: ['7.x' ] 21 | 22 | steps: 23 | 24 | # - name: Install GitVersion 25 | # uses: gittools/actions/gitversion/setup@v0.9.7 26 | # with: 27 | # versionSpec: '5.x' 28 | 29 | - uses: actions/checkout@v2 30 | with: 31 | fetch-depth: 0 32 | 33 | - name: Setup .NET Core SDK ${{ matrix.dotnet-version }} 34 | uses: actions/setup-dotnet@v3 35 | 36 | with: 37 | dotnet-version: ${{ matrix.dotnet-version }} 38 | 39 | - name: Get Release Version 40 | id: package_version 41 | uses: KageKirin/get-csproj-version@v0.0.1 42 | with: 43 | file: ${{ env.PLUGIN_PROJECT }} 44 | regex: ^(?0|[1-9]\d*)\.(?0|[1-9]\d*)\.(?0|[1-9]\d*)(?:-(?(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$ 45 | 46 | - name: Check Version v${{ steps.package_version.outputs.version }} Pre-Release state 47 | id: check_prerelease 48 | run: | 49 | if [[ ${{ steps.package_version.outputs.version }} =~ ^[0-9]+\.[0-9]+\.[0-9]+-.+$ ]]; then 50 | echo Pre-Release detected 51 | echo ::set-output name=prerelease::true 52 | else 53 | echo Pre-Release NOT detected 54 | echo ::set-output name=prerelease::false 55 | fi 56 | 57 | - name: Restore dependencies 58 | run: dotnet restore ${{ env.SOLUTION }} 59 | 60 | - run: dotnet build ${{ env.SOLUTION }} --no-restore --configuration Release 61 | 62 | - name: Create Nuget Packages 63 | run: dotnet pack ${{ env.SOLUTION }} --no-build --no-restore --configuration Release --property:PackageOutputPath=${{ env.OUTPUT_PATH }} 64 | # https://learn.microsoft.com/en-us/dotnet/core/compatibility/sdk/7.0/solution-level-output-no-longer-valid 65 | 66 | - name: Publish to NuGet 67 | working-directory: ${{ env.OUTPUT_PATH }} 68 | run: dotnet nuget push *.nupkg -s https://api.nuget.org/v3/index.json --skip-duplicate --api-key ${{ secrets.NUGET_API_KEY }} 69 | 70 | - uses: avakar/tag-and-release@v1 71 | name: Create Release Tag v${{ steps.package_version.outputs.version }} 72 | id: release 73 | with: 74 | tag_name: v${{ steps.package_version.outputs.version }} 75 | prerelease: ${{ steps.check_prerelease.outputs.prerelease }} 76 | env: 77 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 78 | 79 | - name: Release information for v${{ steps.package_version.outputs.version }} 80 | run: | 81 | echo Release Id: ${{ steps.release.outputs.id }} 82 | echo Upload URL: ${{ steps.release.outputs.upload_url }} 83 | echo Release URL: ${{ steps.release.outputs.html_url }} 84 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Coverlet is a free, cross platform Code Coverage Tool 141 | coverage*[.json, .xml, .info] 142 | 143 | # Visual Studio code coverage results 144 | *.coverage 145 | *.coveragexml 146 | 147 | # NCrunch 148 | _NCrunch_* 149 | .*crunch*.local.xml 150 | nCrunchTemp_* 151 | 152 | # MightyMoose 153 | *.mm.* 154 | AutoTest.Net/ 155 | 156 | # Web workbench (sass) 157 | .sass-cache/ 158 | 159 | # Installshield output folder 160 | [Ee]xpress/ 161 | 162 | # DocProject is a documentation generator add-in 163 | DocProject/buildhelp/ 164 | DocProject/Help/*.HxT 165 | DocProject/Help/*.HxC 166 | DocProject/Help/*.hhc 167 | DocProject/Help/*.hhk 168 | DocProject/Help/*.hhp 169 | DocProject/Help/Html2 170 | DocProject/Help/html 171 | 172 | # Click-Once directory 173 | publish/ 174 | 175 | # Publish Web Output 176 | *.[Pp]ublish.xml 177 | *.azurePubxml 178 | # Note: Comment the next line if you want to checkin your web deploy settings, 179 | # but database connection strings (with potential passwords) will be unencrypted 180 | *.pubxml 181 | *.publishproj 182 | 183 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 184 | # checkin your Azure Web App publish settings, but sensitive information contained 185 | # in these scripts will be unencrypted 186 | PublishScripts/ 187 | 188 | # NuGet Packages 189 | *.nupkg 190 | # NuGet Symbol Packages 191 | *.snupkg 192 | # The packages folder can be ignored because of Package Restore 193 | **/[Pp]ackages/* 194 | # except build/, which is used as an MSBuild target. 195 | !**/[Pp]ackages/build/ 196 | # Uncomment if necessary however generally it will be regenerated when needed 197 | #!**/[Pp]ackages/repositories.config 198 | # NuGet v3's project.json files produces more ignorable files 199 | *.nuget.props 200 | *.nuget.targets 201 | 202 | # Microsoft Azure Build Output 203 | csx/ 204 | *.build.csdef 205 | 206 | # Microsoft Azure Emulator 207 | ecf/ 208 | rcf/ 209 | 210 | # Windows Store app package directories and files 211 | AppPackages/ 212 | BundleArtifacts/ 213 | Package.StoreAssociation.xml 214 | _pkginfo.txt 215 | *.appx 216 | *.appxbundle 217 | *.appxupload 218 | 219 | # Visual Studio cache files 220 | # files ending in .cache can be ignored 221 | *.[Cc]ache 222 | # but keep track of directories ending in .cache 223 | !?*.[Cc]ache/ 224 | 225 | # Others 226 | ClientBin/ 227 | ~$* 228 | *~ 229 | *.dbmdl 230 | *.dbproj.schemaview 231 | *.jfm 232 | *.pfx 233 | *.publishsettings 234 | orleans.codegen.cs 235 | 236 | # Including strong name files can present a security risk 237 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 238 | #*.snk 239 | 240 | # Since there are multiple workflows, uncomment next line to ignore bower_components 241 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 242 | #bower_components/ 243 | 244 | # RIA/Silverlight projects 245 | Generated_Code/ 246 | 247 | # Backup & report files from converting an old project file 248 | # to a newer Visual Studio version. Backup files are not needed, 249 | # because we have git ;-) 250 | _UpgradeReport_Files/ 251 | Backup*/ 252 | UpgradeLog*.XML 253 | UpgradeLog*.htm 254 | ServiceFabricBackup/ 255 | *.rptproj.bak 256 | 257 | # SQL Server files 258 | *.mdf 259 | *.ldf 260 | *.ndf 261 | 262 | # Business Intelligence projects 263 | *.rdl.data 264 | *.bim.layout 265 | *.bim_*.settings 266 | *.rptproj.rsuser 267 | *- [Bb]ackup.rdl 268 | *- [Bb]ackup ([0-9]).rdl 269 | *- [Bb]ackup ([0-9][0-9]).rdl 270 | 271 | # Microsoft Fakes 272 | FakesAssemblies/ 273 | 274 | # GhostDoc plugin setting file 275 | *.GhostDoc.xml 276 | 277 | # Node.js Tools for Visual Studio 278 | .ntvs_analysis.dat 279 | node_modules/ 280 | 281 | # Visual Studio 6 build log 282 | *.plg 283 | 284 | # Visual Studio 6 workspace options file 285 | *.opt 286 | 287 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 288 | *.vbw 289 | 290 | # Visual Studio LightSwitch build output 291 | **/*.HTMLClient/GeneratedArtifacts 292 | **/*.DesktopClient/GeneratedArtifacts 293 | **/*.DesktopClient/ModelManifest.xml 294 | **/*.Server/GeneratedArtifacts 295 | **/*.Server/ModelManifest.xml 296 | _Pvt_Extensions 297 | 298 | # Paket dependency manager 299 | .paket/paket.exe 300 | paket-files/ 301 | 302 | # FAKE - F# Make 303 | .fake/ 304 | 305 | # CodeRush personal settings 306 | .cr/personal 307 | 308 | # Python Tools for Visual Studio (PTVS) 309 | __pycache__/ 310 | *.pyc 311 | 312 | # Cake - Uncomment if you are using it 313 | # tools/** 314 | # !tools/packages.config 315 | 316 | # Tabs Studio 317 | *.tss 318 | 319 | # Telerik's JustMock configuration file 320 | *.jmconfig 321 | 322 | # BizTalk build output 323 | *.btp.cs 324 | *.btm.cs 325 | *.odx.cs 326 | *.xsd.cs 327 | 328 | # OpenCover UI analysis results 329 | OpenCover/ 330 | 331 | # Azure Stream Analytics local run output 332 | ASALocalRun/ 333 | 334 | # MSBuild Binary and Structured Log 335 | *.binlog 336 | 337 | # NVidia Nsight GPU debugger configuration file 338 | *.nvuser 339 | 340 | # MFractors (Xamarin productivity tool) working folder 341 | .mfractor/ 342 | 343 | # Local History for Visual Studio 344 | .localhistory/ 345 | 346 | # BeatPulse healthcheck temp database 347 | healthchecksdb 348 | 349 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 350 | MigrationBackup/ 351 | 352 | # Ionide (cross platform F# VS Code tools) working folder 353 | .ionide/ 354 | 355 | ## Ignore Umbraco files/folders generated for each instance 356 | ## 357 | ## Get latest from https://github.com/github/gitignore/blob/master/Umbraco.gitignore 358 | 359 | # Note: VisualStudio gitignore rules may also be relevant 360 | 361 | # Umbraco 362 | # Ignore unimportant folders generated by Umbraco 363 | **/App_Data/Logs/ 364 | **/App_Data/[Pp]review/ 365 | **/App_Data/TEMP/ 366 | **/App_Data/NuGetBackup/ 367 | 368 | # Ignore Umbraco content cache file 369 | **/App_Data/umbraco.config 370 | 371 | # Don't ignore Umbraco packages (VisualStudio.gitignore mistakes this for a NuGet packages folder) 372 | # Make sure to include details from VisualStudio.gitignore BEFORE this 373 | !**/App_Data/[Pp]ackages/* 374 | !**/[Uu]mbraco/[Dd]eveloper/[Pp]ackages/* 375 | 376 | # ImageProcessor DiskCache 377 | **/App_Data/cache/ 378 | 379 | # Ignore the Models Builder models out of date flag 380 | **/App_Data/Models/ood.flag 381 | 382 | **/._* 383 | /package 384 | /WebSampleV12/App_Plugins/MemberListView 385 | /WebSampleV12/umbraco/Data/TEMP 386 | /WebSampleV12/Views/Partials 387 | -------------------------------------------------------------------------------- /MemberListView.Release.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31825.309 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{181F11FE-4EDD-48D2-B9F5-8C3EF33FA95A}" 7 | ProjectSection(SolutionItems) = preProject 8 | README.md = README.md 9 | umbraco-marketplace.json = umbraco-marketplace.json 10 | EndProjectSection 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MemberListView", "MemberListView\MemberListView.csproj", "{214C964C-014A-418A-A745-EAD4521CEFDA}" 13 | EndProject 14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GitHub", "GitHub", "{3A070101-4361-4CBC-9D3A-5BD0797AB19B}" 15 | ProjectSection(SolutionItems) = preProject 16 | .github\workflows\build.yml = .github\workflows\build.yml 17 | .github\workflows\release.yml = .github\workflows\release.yml 18 | EndProjectSection 19 | EndProject 20 | Global 21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 22 | Debug|Any CPU = Debug|Any CPU 23 | Release|Any CPU = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 26 | {214C964C-014A-418A-A745-EAD4521CEFDA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {214C964C-014A-418A-A745-EAD4521CEFDA}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {214C964C-014A-418A-A745-EAD4521CEFDA}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {214C964C-014A-418A-A745-EAD4521CEFDA}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | GlobalSection(NestedProjects) = preSolution 35 | {3A070101-4361-4CBC-9D3A-5BD0797AB19B} = {181F11FE-4EDD-48D2-B9F5-8C3EF33FA95A} 36 | EndGlobalSection 37 | GlobalSection(ExtensibilityGlobals) = postSolution 38 | SolutionGuid = {5A1FA5B4-E461-46A4-8E83-423F42C07507} 39 | EndGlobalSection 40 | EndGlobal 41 | -------------------------------------------------------------------------------- /MemberListView.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31825.309 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{181F11FE-4EDD-48D2-B9F5-8C3EF33FA95A}" 7 | ProjectSection(SolutionItems) = preProject 8 | README.md = README.md 9 | umbraco-marketplace.json = umbraco-marketplace.json 10 | EndProjectSection 11 | EndProject 12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MemberListView", "MemberListView\MemberListView.csproj", "{214C964C-014A-418A-A745-EAD4521CEFDA}" 13 | EndProject 14 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GitHub", "GitHub", "{3A070101-4361-4CBC-9D3A-5BD0797AB19B}" 15 | ProjectSection(SolutionItems) = preProject 16 | .github\workflows\build.yml = .github\workflows\build.yml 17 | .github\workflows\release.yml = .github\workflows\release.yml 18 | EndProjectSection 19 | EndProject 20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebSample.V11", "WebSample.V11\WebSample.V11.csproj", "{586F2562-DB23-40BC-9FE4-B78E74EF699F}" 21 | EndProject 22 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebSampleV12", "WebSampleV12\WebSampleV12.csproj", "{FF612072-F763-42FD-B105-71ED59EF48FB}" 23 | EndProject 24 | Global 25 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 26 | Debug|Any CPU = Debug|Any CPU 27 | Release|Any CPU = Release|Any CPU 28 | EndGlobalSection 29 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 30 | {214C964C-014A-418A-A745-EAD4521CEFDA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {214C964C-014A-418A-A745-EAD4521CEFDA}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {214C964C-014A-418A-A745-EAD4521CEFDA}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {214C964C-014A-418A-A745-EAD4521CEFDA}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {586F2562-DB23-40BC-9FE4-B78E74EF699F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {586F2562-DB23-40BC-9FE4-B78E74EF699F}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {586F2562-DB23-40BC-9FE4-B78E74EF699F}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {586F2562-DB23-40BC-9FE4-B78E74EF699F}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {FF612072-F763-42FD-B105-71ED59EF48FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {FF612072-F763-42FD-B105-71ED59EF48FB}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {FF612072-F763-42FD-B105-71ED59EF48FB}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {FF612072-F763-42FD-B105-71ED59EF48FB}.Release|Any CPU.Build.0 = Release|Any CPU 42 | EndGlobalSection 43 | GlobalSection(SolutionProperties) = preSolution 44 | HideSolutionNode = FALSE 45 | EndGlobalSection 46 | GlobalSection(NestedProjects) = preSolution 47 | {3A070101-4361-4CBC-9D3A-5BD0797AB19B} = {181F11FE-4EDD-48D2-B9F5-8C3EF33FA95A} 48 | EndGlobalSection 49 | GlobalSection(ExtensibilityGlobals) = postSolution 50 | SolutionGuid = {5A1FA5B4-E461-46A4-8E83-423F42C07507} 51 | EndGlobalSection 52 | EndGlobal 53 | -------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/css/memberListView.css: -------------------------------------------------------------------------------- 1 | .umb-listview.membermanager a { 2 | text-decoration: none; } 3 | 4 | .umb-listview.membermanager .umb-table__name { 5 | -ms-flex: 1 1 5%; 6 | flex: 1 1 5%; 7 | max-width: 20%; } 8 | 9 | .umb-listview.membermanager .no-results { 10 | padding: 20px; 11 | background-color: #f8f8f8; 12 | border: 1px solid #dbdbdb; 13 | text-align: center; 14 | color: #999; 15 | margin-top: 20px; } 16 | 17 | .filter-display { 18 | padding: 1em; 19 | background-color: #f8f8f8; 20 | border: 1px solid #dbdbdb; 21 | margin-bottom: 1em; } 22 | .filter-display > div { 23 | overflow: auto; 24 | margin-bottom: 5px; } 25 | .filter-display > div:last-child { 26 | margin-bottom: 0; } 27 | .filter-display > div strong { 28 | float: left; 29 | width: 180px; 30 | display: block; } 31 | .filter-display > div span { 32 | float: left; 33 | width: auto; } 34 | 35 | .control-flow-horizontal .control-label { 36 | float: none; } 37 | 38 | .control-flow-horizontal .controls.controls-row { 39 | margin-left: 0px; } 40 | 41 | .filter-options .filter-display { 42 | margin-bottom: 0; } 43 | 44 | .filter-options .filter-checkbox-list { 45 | margin-bottom: 1em; } 46 | .filter-options .filter-checkbox-list .filter-content { 47 | max-height: 10.4em; 48 | overflow-y: scroll; 49 | border: solid 1px #eee; 50 | border-radius: .25em; } 51 | .filter-options .filter-checkbox-list .filter-content, .filter-options .filter-checkbox-list .filter-header { 52 | padding: .5em; } 53 | .filter-options .filter-checkbox-list .filter-content > div, .filter-options .filter-checkbox-list .filter-header > div { 54 | overflow: auto; } 55 | .filter-options .filter-checkbox-list .filter-content > div input[type="checkbox"], .filter-options .filter-checkbox-list .filter-header > div input[type="checkbox"] { 56 | float: left; 57 | /*margin-top: 10px;*/ 58 | margin-right: 10px; } 59 | .filter-options .filter-checkbox-list .filter-content > div label.control-label, .filter-options .filter-checkbox-list .filter-header > div label.control-label { 60 | float: left; 61 | margin: 0; 62 | padding: 0; 63 | width: auto; 64 | width: 90%; 65 | font-weight: 400; } 66 | .filter-options .filter-checkbox-list .filter-header { 67 | margin-bottom: .5em; } 68 | -------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/css/memberListView.min.css: -------------------------------------------------------------------------------- 1 | .umb-listview.membermanager a{text-decoration:none;}.umb-listview.membermanager .umb-table__name{-ms-flex:1 1 5%;flex:1 1 5%;max-width:20%;}.umb-listview.membermanager .no-results{padding:20px;background-color:#f8f8f8;border:1px solid #dbdbdb;text-align:center;color:#999;margin-top:20px;}.filter-display{padding:1em;background-color:#f8f8f8;border:1px solid #dbdbdb;margin-bottom:1em;}.filter-display>div{overflow:auto;margin-bottom:5px;}.filter-display>div:last-child{margin-bottom:0;}.filter-display>div strong{float:left;width:180px;display:block;}.filter-display>div span{float:left;width:auto;}.control-flow-horizontal .control-label{float:none;}.control-flow-horizontal .controls.controls-row{margin-left:0;}.filter-options .filter-display{margin-bottom:0;}.filter-options .filter-checkbox-list{margin-bottom:1em;}.filter-options .filter-checkbox-list .filter-content{max-height:10.4em;overflow-y:scroll;border:solid 1px #eee;border-radius:.25em;}.filter-options .filter-checkbox-list .filter-content,.filter-options .filter-checkbox-list .filter-header{padding:.5em;}.filter-options .filter-checkbox-list .filter-content>div,.filter-options .filter-checkbox-list .filter-header>div{overflow:auto;}.filter-options .filter-checkbox-list .filter-content>div input[type="checkbox"],.filter-options .filter-checkbox-list .filter-header>div input[type="checkbox"]{float:left;margin-right:10px;}.filter-options .filter-checkbox-list .filter-content>div label.control-label,.filter-options .filter-checkbox-list .filter-header>div label.control-label{float:left;margin:0;padding:0;width:auto;width:90%;font-weight:400;}.filter-options .filter-checkbox-list .filter-header{margin-bottom:.5em;} -------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/dashboard/memberManager.html: -------------------------------------------------------------------------------- 1 | 
2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 15 |
16 | 17 | 18 |
19 | 23 | 24 | 25 | 26 | 30 | 31 | 32 |
33 | 34 |
35 | 36 | 37 | 42 | 43 | 44 | 45 | 46 | {{ selectedItemsCount() }} of {{ listViewResultSet.items.length }} selected 47 | 48 | 49 | 50 | 51 | 52 |
53 | 54 | 55 | 56 | 57 | 60 | 61 | 62 | 69 | 70 | 71 | 79 | 80 | 81 | 82 | 83 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 104 | 105 | 106 | 114 | 115 | 116 | 124 | 125 | 126 | 134 | 135 | 136 | 137 | 138 | 139 |
140 |
141 | 142 | 143 | 144 |
145 |
146 | {{displayFilter.title}} 147 | {{displayFilter.value}} 148 |
149 |
150 |
151 |
152 | 153 | 161 | 162 | 163 | 164 | 165 |
166 | 172 | 173 |
174 | 175 | 179 | 180 |
-------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/dialogs/member/export.controller.js: -------------------------------------------------------------------------------- 1 | angular.module("umbraco").controller("MemberManager.Dialogs.Member.ExportController", 2 | function ($scope, memberListViewResource) { 3 | "use strict"; 4 | 5 | $scope.vm = { 6 | filterData: $scope.model.filterData, 7 | exportData: { 8 | format: $scope.model.format ?? "Excel", 9 | columns: $scope.model.columns 10 | }, 11 | totalItems: $scope.model.totalItems, 12 | memberTypes: $scope.model.memberTypes, 13 | columnList: [] 14 | } 15 | 16 | function init() { 17 | memberListViewResource.getMemberColumns($scope.vm.filterData.memberType).then(function (data) { 18 | // We use this to preserve the original filter data. 19 | $scope.vm.columnList = setSelected(data, $scope.vm.exportData.columns) 20 | }) 21 | 22 | } 23 | 24 | // Loop through a list of select options and set selected for values that appear in a filter list 25 | const setSelected = function (list, filter) { 26 | // Convert string arrays to a select item object. 27 | const newList = _.map(list, function (item, index, list) { 28 | if (typeof list[index] === "string") { 29 | let value = item 30 | item = { 31 | id: index, 32 | name: value, 33 | alias: value, 34 | selected: false 35 | } 36 | } else { 37 | item = list[index] 38 | } 39 | return item 40 | }) 41 | 42 | if (filter) { 43 | if (Array.isArray(filter)) { 44 | for (var i = 0; i < filter.length; i++) { 45 | for (var j = 0; j < newList.length; j++) { 46 | if (newList[j].id === filter[i].replace(" ", "_")) { 47 | newList[j].selected = true 48 | } 49 | } 50 | } 51 | } else { 52 | for (var i = 0; i < newList.length; i++) { 53 | if (newList[i].id === filter.replace(" ", "_")) { 54 | newList[i].selected = true 55 | } 56 | } 57 | } 58 | } 59 | 60 | return newList } 61 | 62 | const processColumnList = function (list) { 63 | var filteredList = _.filter(list, function (i) { 64 | return i.selected 65 | }) 66 | 67 | return _.map(filteredList, function (item) { 68 | return item.alias 69 | }) 70 | } 71 | 72 | // Methods to manage select/deselect all in checkbox lists. 73 | $scope.selectAll = function (selectionList) { 74 | const allSelected = $scope.allSelected(selectionList) 75 | _.each(selectionList, function (item) { item.selected = !allSelected }) 76 | } 77 | 78 | $scope.allSelected = function (selectionList) { 79 | return _.filter(selectionList, function (item) { return item.selected }).length === selectionList.length 80 | } 81 | 82 | $scope.exportRecords = function () { 83 | $scope.vm.exportData.columns = processColumnList($scope.vm.columnList) 84 | 85 | $scope.model.submit($scope.vm.exportData) 86 | } 87 | 88 | init() 89 | }) -------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/dialogs/member/export.html: -------------------------------------------------------------------------------- 1 | 
2 | 3 | 4 | 5 |
6 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | {{vm.totalItems}} members 20 |
21 | Exporting a large number of members may take some time. 22 |
23 |
24 | 25 | 27 |
28 |
29 | {{displayFilter.title}} 30 | {{displayFilter.value}} 31 |
32 |
33 |
34 |
35 | 36 | 39 |
40 |
41 |
42 | 46 |
47 |
48 |
49 |
50 | 54 |
55 |
56 |
57 |
58 |
59 | 60 | 61 | 65 | 66 | 67 |
68 |
69 |
70 | 71 | 72 | 73 | 78 | 79 | 83 | 84 | 85 | 86 | 87 |
88 |
89 |
-------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/dialogs/member/filter.controller.js: -------------------------------------------------------------------------------- 1 | angular.module("umbraco").controller("MemberManager.Dialogs.Member.FilterController", 2 | function ($scope) { 3 | "use strict"; 4 | 5 | $scope.defaultButton = null 6 | $scope.subButtons = [] 7 | 8 | $scope.vm = { 9 | filterData: $scope.model.filterData, 10 | // Use setSelected when you have a property that can have multiple selections in the filter. 11 | memberGroups: setSelected($scope.model.memberGroups, $scope.model.filterData.memberGroups), 12 | memberTypes: setSelected($scope.model.memberTypes, $scope.model.filterData.memberType) 13 | } 14 | 15 | if (!$scope.vm.filterData.memberType) { 16 | $scope.vm.filterData.memberType = '' 17 | } 18 | 19 | function init() { 20 | var buttons = { 21 | defaultButton: createButtonDefinition("F"), 22 | subButtons: [ 23 | createButtonDefinition("C") 24 | ] 25 | } 26 | 27 | $scope.defaultButton = buttons.defaultButton 28 | $scope.subButtons = buttons.subButtons 29 | } 30 | 31 | function createButtonDefinition(ch) { 32 | switch (ch) { 33 | case "F": 34 | //publish action 35 | return { 36 | letter: ch, 37 | labelKey: "memberManager_applyFilter", 38 | label: "Apply Filter", 39 | handler: $scope.applyFilter, 40 | hotKey: "ctrl+f", 41 | hotKeyWhenHidden: true, 42 | alias: "applyFilter" 43 | } 44 | case "C": 45 | //send to publish 46 | return { 47 | letter: ch, 48 | labelKey: "memberManager_clearFilter", 49 | label: "Clear Filter", 50 | handler: $scope.clearFilter, 51 | hotKey: "ctrl+c", 52 | hotKeyWhenHidden: true, 53 | alias: "clearFilter" 54 | } 55 | default: 56 | return null 57 | } 58 | } 59 | 60 | // Generate model for representing the search 61 | function getFilterModel() { 62 | var displaySearch = [] 63 | 64 | //if ($scope.filterData.filter) { 65 | // displaySearch.push({ title: "Search", value: $scope.filterData.filter }) 66 | //} 67 | if ($scope.vm.filterData.memberType && $scope.vm.filterData.memberType.length > 0) { 68 | displaySearch.push({ title: "Member Type", value: getMemberTypeName() }) 69 | } 70 | if ($scope.vm.filterData.umbracoMemberApproved === 1 || $scope.vm.filterData.umbracoMemberApproved === 0) { 71 | displaySearch.push({ title: "Approved", value: $scope.vm.filterData.umbracoMemberApproved === 1 ? "Approved" : "Suspended" }) 72 | } 73 | 74 | if ($scope.vm.filterData.umbracoMemberLockedOut === 1 || $scope.vm.filterData.umbracoMemberLockedOut === 0) { 75 | displaySearch.push({ title: "Locked Out", value: $scope.vm.filterData.umbracoMemberLockedOut === 1 ? "Locked Out" : "Active" }) 76 | } 77 | 78 | return { 79 | filter: $scope.vm.filterData.filter, 80 | memberType: $scope.vm.filterData.memberType, 81 | memberGroups: processFilterList(displaySearch, "Member Groups", $scope.vm.memberGroups, true), 82 | umbracoMemberApproved: $scope.vm.filterData.umbracoMemberApproved, 83 | umbracoMemberLockedOut: $scope.vm.filterData.umbracoMemberLockedOut, 84 | 85 | // Additional filters should have the f_ prefix and should match the alias of the member property. 86 | 87 | display: displaySearch 88 | } 89 | } 90 | 91 | function getMemberTypeName() { 92 | 93 | var type = _.filter($scope.vm.memberTypes, function (item) { 94 | return item.alias === $scope.vm.filterData.memberType 95 | }) 96 | 97 | return _.map(type, function (item) { 98 | return ' ' + item.name 99 | }).join() 100 | 101 | } 102 | 103 | // Loop through a list of select options and set selected for values that appear in a filter list 104 | function setSelected(list, filter) { 105 | // Convert string arrays to a select item object. 106 | const newList = _.map(list, function (item, index, list) { 107 | if (typeof list[index] === "string") { 108 | let value = item 109 | item = { 110 | id: index, 111 | name: value, 112 | alias: value, 113 | selected: false 114 | } 115 | } else { 116 | item = list[index] 117 | } 118 | return item 119 | }) 120 | 121 | if (filter) { 122 | if (Array.isArray(filter)) { 123 | for (var i = 0; i < filter.length; i++) { 124 | for (var j = 0; j < newList.length; j++) { 125 | if (newList[j].id === filter[i].replace(" ", "_")) { 126 | newList[j].selected = true 127 | } 128 | } 129 | } 130 | } else { 131 | for (var i = 0; i < newList.length; i++) { 132 | if (newList[i].id === filter.replace(" ", "_")) { 133 | newList[i].selected = true 134 | } 135 | } 136 | } 137 | } 138 | 139 | return newList 140 | } 141 | 142 | 143 | function processFilterList (display, title, list, useId = false) { 144 | var filteredList = _.filter(list, function (i) { 145 | return i.selected 146 | }) 147 | 148 | var displayVal = _.map(filteredList, function (item) { 149 | return ' ' + item.name 150 | }).join() 151 | 152 | if (displayVal) { 153 | display.push({ title: title, value: displayVal }) 154 | } 155 | return _.map(filteredList, function (item) { 156 | return useId ? item.id : item.alias 157 | }) 158 | } 159 | 160 | $scope.applyFilter = function () { 161 | $scope.model.submit(getFilterModel()) 162 | } 163 | 164 | $scope.clearFilter = function () { 165 | $scope.model.submit({ 166 | filter: null 167 | }) 168 | } 169 | 170 | init() 171 | }) -------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/dialogs/member/filter.html: -------------------------------------------------------------------------------- 1 | 
2 | 3 | 4 |
5 | 6 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 23 | 27 | 28 | 29 | 32 |
33 |
34 |
35 | 39 |
40 |
41 |
42 |
43 |
44 | 45 | 46 | 51 | 52 | 53 | 54 | 55 | 60 | 61 | 62 |
63 |
64 |
65 | 66 | 67 | 68 | 73 | 74 | 75 | 79 | 80 | 81 | 82 | 83 |
84 | 85 |
86 |
-------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/lang/cs.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Robert Foster 5 | 6 | 7 | 8 | Manage Members 9 | 10 | 11 | Approved 12 | Locked 13 | Groups 14 | 15 | 16 | Approve 17 | Suspend 18 | Apply Filter 19 | Clear Filter 20 | 21 | 22 | Unlocking %0% Member 23 | Unlocking %0% of %1% Members 24 | Unlocked %0% Member 25 | Unlocked %0% Members 26 | Suspending %0% Member 27 | Suspending %0% of %1% Members 28 | Suspended %0% Member 29 | Suspended %0% Members 30 | Approving %0% Member 31 | Approving %0% of %1% Members 32 | Approved %0% Member 33 | Approved %0% Members 34 | 35 | 36 | Filter 37 | Clear Filter 38 | Export Members 39 | Export 40 | Apply Filter 41 | Exporting large numbers of members may take some time. Are you sure you want to continue? 42 | Are you sure you want to unlock the selected members? 43 | Are you sure you want to suspend the selected members? 44 | Are you sure you want to approve the selected members? 45 | Suspended 46 | Approved 47 | Locked Out 48 | Unlocked 49 | Yes, approve 50 | Yes, suspend 51 | Yes, unlock 52 | Exportable records 53 | Exporting a large number of members may take some time. 54 | %0% members 55 | Applied Filters 56 | Columns 57 | Format 58 | Select the columns to include in the exported data 59 | 60 | 61 | -------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/lang/da.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Robert Foster 5 | 6 | 7 | 8 | Manage Members 9 | 10 | 11 | Approved 12 | Locked 13 | Groups 14 | 15 | 16 | Approve 17 | Suspend 18 | Apply Filter 19 | Clear Filter 20 | 21 | 22 | Unlocking %0% Member 23 | Unlocking %0% of %1% Members 24 | Unlocked %0% Member 25 | Unlocked %0% Members 26 | Suspending %0% Member 27 | Suspending %0% of %1% Members 28 | Suspended %0% Member 29 | Suspended %0% Members 30 | Approving %0% Member 31 | Approving %0% of %1% Members 32 | Approved %0% Member 33 | Approved %0% Members 34 | 35 | 36 | Filter 37 | Clear Filter 38 | Export Members 39 | Export 40 | Apply Filter 41 | Exporting large numbers of members may take some time. Are you sure you want to continue? 42 | Are you sure you want to unlock the selected members? 43 | Are you sure you want to suspend the selected members? 44 | Are you sure you want to approve the selected members? 45 | Suspended 46 | Approved 47 | Locked Out 48 | Unlocked 49 | Yes, approve 50 | Yes, suspend 51 | Yes, unlock 52 | Exportable records 53 | Exporting a large number of members may take some time. 54 | %0% members 55 | Applied Filters 56 | Columns 57 | Format 58 | Select the columns to include in the exported data 59 | 60 | 61 | -------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/lang/de.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Robert Foster 5 | 6 | 7 | 8 | Manage Members 9 | 10 | 11 | Approved 12 | Locked 13 | Groups 14 | 15 | 16 | Approve 17 | Suspend 18 | Apply Filter 19 | Clear Filter 20 | 21 | 22 | Unlocking %0% Member 23 | Unlocking %0% of %1% Members 24 | Unlocked %0% Member 25 | Unlocked %0% Members 26 | Suspending %0% Member 27 | Suspending %0% of %1% Members 28 | Suspended %0% Member 29 | Suspended %0% Members 30 | Approving %0% Member 31 | Approving %0% of %1% Members 32 | Approved %0% Member 33 | Approved %0% Members 34 | 35 | 36 | Filter 37 | Clear Filter 38 | Export Members 39 | Export 40 | Apply Filter 41 | Exporting large numbers of members may take some time. Are you sure you want to continue? 42 | Are you sure you want to unlock the selected members? 43 | Are you sure you want to suspend the selected members? 44 | Are you sure you want to approve the selected members? 45 | Suspended 46 | Approved 47 | Locked Out 48 | Unlocked 49 | Yes, approve 50 | Yes, suspend 51 | Yes, unlock 52 | Exportable records 53 | Exporting a large number of members may take some time. 54 | %0% members 55 | Applied Filters 56 | Columns 57 | Format 58 | Select the columns to include in the exported data 59 | 60 | 61 | -------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/lang/en-gb.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Robert Foster 5 | 6 | 7 | 8 | Manage Members 9 | 10 | 11 | Approved 12 | Locked 13 | Groups 14 | 15 | 16 | Approve 17 | Suspend 18 | Apply Filter 19 | Clear Filter 20 | 21 | 22 | Unlocking %0% Member 23 | Unlocking %0% of %1% Members 24 | Unlocked %0% Member 25 | Unlocked %0% Members 26 | Suspending %0% Member 27 | Suspending %0% of %1% Members 28 | Suspended %0% Member 29 | Suspended %0% Members 30 | Approving %0% Member 31 | Approving %0% of %1% Members 32 | Approved %0% Member 33 | Approved %0% Members 34 | 35 | 36 | Filter 37 | Clear Filter 38 | Export Members 39 | Export 40 | Apply Filter 41 | Exporting large numbers of members may take some time. Are you sure you want to continue? 42 | Are you sure you want to unlock the selected members? 43 | Are you sure you want to suspend the selected members? 44 | Are you sure you want to approve the selected members? 45 | Suspended 46 | Approved 47 | Locked Out 48 | Unlocked 49 | Yes, approve 50 | Yes, suspend 51 | Yes, unlock 52 | Exportable records 53 | Exporting a large number of members may take some time. 54 | %0% members 55 | Applied Filters 56 | Columns 57 | Format 58 | Select the columns to include in the exported data 59 | 60 | 61 | -------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/lang/en-us.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Robert Foster 5 | 6 | 7 | 8 | Manage Members 9 | 10 | 11 | Approved 12 | Locked 13 | Groups 14 | 15 | 16 | Approve 17 | Suspend 18 | Apply Filter 19 | Clear Filter 20 | 21 | 22 | Unlocking %0% Member 23 | Unlocking %0% of %1% Members 24 | Unlocked %0% Member 25 | Unlocked %0% Members 26 | Suspending %0% Member 27 | Suspending %0% of %1% Members 28 | Suspended %0% Member 29 | Suspended %0% Members 30 | Approving %0% Member 31 | Approving %0% of %1% Members 32 | Approved %0% Member 33 | Approved %0% Members 34 | 35 | 36 | Filter 37 | Clear Filter 38 | Export Members 39 | Export 40 | Apply Filter 41 | Exporting large numbers of members may take some time. Are you sure you want to continue? 42 | Are you sure you want to unlock the selected members? 43 | Are you sure you want to suspend the selected members? 44 | Are you sure you want to approve the selected members? 45 | Suspended 46 | Approved 47 | Locked Out 48 | Unlocked 49 | Yes, approve 50 | Yes, suspend 51 | Yes, unlock 52 | Exportable records 53 | Exporting a large number of members may take some time. 54 | %0% members 55 | Applied Filters 56 | Columns 57 | Format 58 | Select the columns to include in the exported data 59 | Member Type 60 | Member Groups 61 | - All - 62 | Approved Only 63 | Suspended Only 64 | Locked Out Only 65 | Unlocked Only 66 | 67 | 68 | -------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/lang/es.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Robert Foster 5 | 6 | 7 | 8 | Manage Members 9 | 10 | 11 | Approved 12 | Locked 13 | Groups 14 | 15 | 16 | Approve 17 | Suspend 18 | Apply Filter 19 | Clear Filter 20 | 21 | 22 | Unlocking %0% Member 23 | Unlocking %0% of %1% Members 24 | Unlocked %0% Member 25 | Unlocked %0% Members 26 | Suspending %0% Member 27 | Suspending %0% of %1% Members 28 | Suspended %0% Member 29 | Suspended %0% Members 30 | Approving %0% Member 31 | Approving %0% of %1% Members 32 | Approved %0% Member 33 | Approved %0% Members 34 | 35 | 36 | Filter 37 | Clear Filter 38 | Export Members 39 | Export 40 | Apply Filter 41 | Exporting large numbers of members may take some time. Are you sure you want to continue? 42 | Are you sure you want to unlock the selected members? 43 | Are you sure you want to suspend the selected members? 44 | Are you sure you want to approve the selected members? 45 | Suspended 46 | Approved 47 | Locked Out 48 | Unlocked 49 | Yes, approve 50 | Yes, suspend 51 | Yes, unlock 52 | Exportable records 53 | Exporting a large number of members may take some time. 54 | %0% members 55 | Applied Filters 56 | Columns 57 | Format 58 | Select the columns to include in the exported data 59 | 60 | 61 | -------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/lang/fr.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Robert Foster 5 | 6 | 7 | 8 | Manage Members 9 | 10 | 11 | Approved 12 | Locked 13 | Groups 14 | 15 | 16 | Approve 17 | Suspend 18 | Apply Filter 19 | Clear Filter 20 | 21 | 22 | Unlocking %0% Member 23 | Unlocking %0% of %1% Members 24 | Unlocked %0% Member 25 | Unlocked %0% Members 26 | Suspending %0% Member 27 | Suspending %0% of %1% Members 28 | Suspended %0% Member 29 | Suspended %0% Members 30 | Approving %0% Member 31 | Approving %0% of %1% Members 32 | Approved %0% Member 33 | Approved %0% Members 34 | 35 | 36 | Filter 37 | Clear Filter 38 | Export Members 39 | Export 40 | Apply Filter 41 | Exporting large numbers of members may take some time. Are you sure you want to continue? 42 | Are you sure you want to unlock the selected members? 43 | Are you sure you want to suspend the selected members? 44 | Are you sure you want to approve the selected members? 45 | Suspended 46 | Approved 47 | Locked Out 48 | Unlocked 49 | Yes, approve 50 | Yes, suspend 51 | Yes, unlock 52 | Exportable records 53 | Exporting a large number of members may take some time. 54 | %0% members 55 | Applied Filters 56 | Columns 57 | Format 58 | Select the columns to include in the exported data 59 | 60 | 61 | -------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/lang/he.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Robert Foster 5 | 6 | 7 | 8 | Manage Members 9 | 10 | 11 | Approved 12 | Locked 13 | Groups 14 | 15 | 16 | Approve 17 | Suspend 18 | Apply Filter 19 | Clear Filter 20 | 21 | 22 | Unlocking %0% Member 23 | Unlocking %0% of %1% Members 24 | Unlocked %0% Member 25 | Unlocked %0% Members 26 | Suspending %0% Member 27 | Suspending %0% of %1% Members 28 | Suspended %0% Member 29 | Suspended %0% Members 30 | Approving %0% Member 31 | Approving %0% of %1% Members 32 | Approved %0% Member 33 | Approved %0% Members 34 | 35 | 36 | Filter 37 | Clear Filter 38 | Export Members 39 | Export 40 | Apply Filter 41 | Exporting large numbers of members may take some time. Are you sure you want to continue? 42 | Are you sure you want to unlock the selected members? 43 | Are you sure you want to suspend the selected members? 44 | Are you sure you want to approve the selected members? 45 | Suspended 46 | Approved 47 | Locked Out 48 | Unlocked 49 | Yes, approve 50 | Yes, suspend 51 | Yes, unlock 52 | Exportable records 53 | Exporting a large number of members may take some time. 54 | %0% members 55 | Applied Filters 56 | Columns 57 | Format 58 | Select the columns to include in the exported data 59 | 60 | 61 | -------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/lang/it.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Robert Foster 5 | 6 | 7 | 8 | Manage Members 9 | 10 | 11 | Approved 12 | Locked 13 | Groups 14 | 15 | 16 | Approve 17 | Suspend 18 | Apply Filter 19 | Clear Filter 20 | 21 | 22 | Unlocking %0% Member 23 | Unlocking %0% of %1% Members 24 | Unlocked %0% Member 25 | Unlocked %0% Members 26 | Suspending %0% Member 27 | Suspending %0% of %1% Members 28 | Suspended %0% Member 29 | Suspended %0% Members 30 | Approving %0% Member 31 | Approving %0% of %1% Members 32 | Approved %0% Member 33 | Approved %0% Members 34 | 35 | 36 | Filter 37 | Clear Filter 38 | Export Members 39 | Export 40 | Apply Filter 41 | Exporting large numbers of members may take some time. Are you sure you want to continue? 42 | Are you sure you want to unlock the selected members? 43 | Are you sure you want to suspend the selected members? 44 | Are you sure you want to approve the selected members? 45 | Suspended 46 | Approved 47 | Locked Out 48 | Unlocked 49 | Yes, approve 50 | Yes, suspend 51 | Yes, unlock 52 | Exportable records 53 | Exporting a large number of members may take some time. 54 | %0% members 55 | Applied Filters 56 | Columns 57 | Format 58 | Select the columns to include in the exported data 59 | 60 | 61 | -------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/lang/ja.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Robert Foster 5 | 6 | 7 | 8 | Manage Members 9 | 10 | 11 | Approved 12 | Locked 13 | Groups 14 | 15 | 16 | Approve 17 | Suspend 18 | Apply Filter 19 | Clear Filter 20 | 21 | 22 | Unlocking %0% Member 23 | Unlocking %0% of %1% Members 24 | Unlocked %0% Member 25 | Unlocked %0% Members 26 | Suspending %0% Member 27 | Suspending %0% of %1% Members 28 | Suspended %0% Member 29 | Suspended %0% Members 30 | Approving %0% Member 31 | Approving %0% of %1% Members 32 | Approved %0% Member 33 | Approved %0% Members 34 | 35 | 36 | Filter 37 | Clear Filter 38 | Export Members 39 | Export 40 | Apply Filter 41 | Exporting large numbers of members may take some time. Are you sure you want to continue? 42 | Are you sure you want to unlock the selected members? 43 | Are you sure you want to suspend the selected members? 44 | Are you sure you want to approve the selected members? 45 | Suspended 46 | Approved 47 | Locked Out 48 | Unlocked 49 | Yes, approve 50 | Yes, suspend 51 | Yes, unlock 52 | Exportable records 53 | Exporting a large number of members may take some time. 54 | %0% members 55 | Applied Filters 56 | Columns 57 | Format 58 | Select the columns to include in the exported data 59 | 60 | 61 | -------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/lang/ko.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Robert Foster 5 | 6 | 7 | 8 | Manage Members 9 | 10 | 11 | Approved 12 | Locked 13 | Groups 14 | 15 | 16 | Approve 17 | Suspend 18 | Apply Filter 19 | Clear Filter 20 | 21 | 22 | Unlocking %0% Member 23 | Unlocking %0% of %1% Members 24 | Unlocked %0% Member 25 | Unlocked %0% Members 26 | Suspending %0% Member 27 | Suspending %0% of %1% Members 28 | Suspended %0% Member 29 | Suspended %0% Members 30 | Approving %0% Member 31 | Approving %0% of %1% Members 32 | Approved %0% Member 33 | Approved %0% Members 34 | 35 | 36 | Filter 37 | Clear Filter 38 | Export Members 39 | Export 40 | Apply Filter 41 | Exporting large numbers of members may take some time. Are you sure you want to continue? 42 | Are you sure you want to unlock the selected members? 43 | Are you sure you want to suspend the selected members? 44 | Are you sure you want to approve the selected members? 45 | Suspended 46 | Approved 47 | Locked Out 48 | Unlocked 49 | Yes, approve 50 | Yes, suspend 51 | Yes, unlock 52 | Exportable records 53 | Exporting a large number of members may take some time. 54 | %0% members 55 | Applied Filters 56 | Columns 57 | Format 58 | Select the columns to include in the exported data 59 | 60 | 61 | -------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/lang/nb.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Robert Foster 5 | 6 | 7 | 8 | Manage Members 9 | 10 | 11 | Approved 12 | Locked 13 | Groups 14 | 15 | 16 | Approve 17 | Suspend 18 | Apply Filter 19 | Clear Filter 20 | 21 | 22 | Unlocking %0% Member 23 | Unlocking %0% of %1% Members 24 | Unlocked %0% Member 25 | Unlocked %0% Members 26 | Suspending %0% Member 27 | Suspending %0% of %1% Members 28 | Suspended %0% Member 29 | Suspended %0% Members 30 | Approving %0% Member 31 | Approving %0% of %1% Members 32 | Approved %0% Member 33 | Approved %0% Members 34 | 35 | 36 | Filter 37 | Clear Filter 38 | Export Members 39 | Export 40 | Apply Filter 41 | Exporting large numbers of members may take some time. Are you sure you want to continue? 42 | Are you sure you want to unlock the selected members? 43 | Are you sure you want to suspend the selected members? 44 | Are you sure you want to approve the selected members? 45 | Suspended 46 | Approved 47 | Locked Out 48 | Unlocked 49 | Yes, approve 50 | Yes, suspend 51 | Yes, unlock 52 | Exportable records 53 | Exporting a large number of members may take some time. 54 | %0% members 55 | Applied Filters 56 | Columns 57 | Format 58 | Select the columns to include in the exported data 59 | 60 | 61 | -------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/lang/nl.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Robert Foster 5 | 6 | 7 | 8 | Manage Members 9 | 10 | 11 | Approved 12 | Locked 13 | Groups 14 | 15 | 16 | Approve 17 | Suspend 18 | Apply Filter 19 | Clear Filter 20 | 21 | 22 | Unlocking %0% Member 23 | Unlocking %0% of %1% Members 24 | Unlocked %0% Member 25 | Unlocked %0% Members 26 | Suspending %0% Member 27 | Suspending %0% of %1% Members 28 | Suspended %0% Member 29 | Suspended %0% Members 30 | Approving %0% Member 31 | Approving %0% of %1% Members 32 | Approved %0% Member 33 | Approved %0% Members 34 | 35 | 36 | Filter 37 | Clear Filter 38 | Export Members 39 | Export 40 | Apply Filter 41 | Exporting large numbers of members may take some time. Are you sure you want to continue? 42 | Are you sure you want to unlock the selected members? 43 | Are you sure you want to suspend the selected members? 44 | Are you sure you want to approve the selected members? 45 | Suspended 46 | Approved 47 | Locked Out 48 | Unlocked 49 | Yes, approve 50 | Yes, suspend 51 | Yes, unlock 52 | Exportable records 53 | Exporting a large number of members may take some time. 54 | %0% members 55 | Applied Filters 56 | Columns 57 | Format 58 | Select the columns to include in the exported data 59 | 60 | 61 | -------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/lang/pl.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Robert Foster 5 | 6 | 7 | 8 | Manage Members 9 | 10 | 11 | Approved 12 | Locked 13 | Groups 14 | 15 | 16 | Approve 17 | Suspend 18 | Apply Filter 19 | Clear Filter 20 | 21 | 22 | Unlocking %0% Member 23 | Unlocking %0% of %1% Members 24 | Unlocked %0% Member 25 | Unlocked %0% Members 26 | Suspending %0% Member 27 | Suspending %0% of %1% Members 28 | Suspended %0% Member 29 | Suspended %0% Members 30 | Approving %0% Member 31 | Approving %0% of %1% Members 32 | Approved %0% Member 33 | Approved %0% Members 34 | 35 | 36 | Filter 37 | Clear Filter 38 | Export Members 39 | Export 40 | Apply Filter 41 | Exporting large numbers of members may take some time. Are you sure you want to continue? 42 | Are you sure you want to unlock the selected members? 43 | Are you sure you want to suspend the selected members? 44 | Are you sure you want to approve the selected members? 45 | Suspended 46 | Approved 47 | Locked Out 48 | Unlocked 49 | Yes, approve 50 | Yes, suspend 51 | Yes, unlock 52 | Exportable records 53 | Exporting a large number of members may take some time. 54 | %0% members 55 | Applied Filters 56 | Columns 57 | Format 58 | Select the columns to include in the exported data 59 | 60 | 61 | -------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/lang/pt.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Robert Foster 5 | 6 | 7 | 8 | Manage Members 9 | 10 | 11 | Approved 12 | Locked 13 | Groups 14 | 15 | 16 | Approve 17 | Suspend 18 | Apply Filter 19 | Clear Filter 20 | 21 | 22 | Unlocking %0% Member 23 | Unlocking %0% of %1% Members 24 | Unlocked %0% Member 25 | Unlocked %0% Members 26 | Suspending %0% Member 27 | Suspending %0% of %1% Members 28 | Suspended %0% Member 29 | Suspended %0% Members 30 | Approving %0% Member 31 | Approving %0% of %1% Members 32 | Approved %0% Member 33 | Approved %0% Members 34 | 35 | 36 | Filter 37 | Clear Filter 38 | Export Members 39 | Export 40 | Apply Filter 41 | Exporting large numbers of members may take some time. Are you sure you want to continue? 42 | Are you sure you want to unlock the selected members? 43 | Are you sure you want to suspend the selected members? 44 | Are you sure you want to approve the selected members? 45 | Suspended 46 | Approved 47 | Locked Out 48 | Unlocked 49 | Yes, approve 50 | Yes, suspend 51 | Yes, unlock 52 | Exportable records 53 | Exporting a large number of members may take some time. 54 | %0% members 55 | Applied Filters 56 | Columns 57 | Format 58 | Select the columns to include in the exported data 59 | 60 | 61 | -------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/lang/ru.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Robert Foster 5 | 6 | 7 | 8 | Manage Members 9 | 10 | 11 | Approved 12 | Locked 13 | Groups 14 | 15 | 16 | Approve 17 | Suspend 18 | Apply Filter 19 | Clear Filter 20 | 21 | 22 | Unlocking %0% Member 23 | Unlocking %0% of %1% Members 24 | Unlocked %0% Member 25 | Unlocked %0% Members 26 | Suspending %0% Member 27 | Suspending %0% of %1% Members 28 | Suspended %0% Member 29 | Suspended %0% Members 30 | Approving %0% Member 31 | Approving %0% of %1% Members 32 | Approved %0% Member 33 | Approved %0% Members 34 | 35 | 36 | Filter 37 | Clear Filter 38 | Export Members 39 | Export 40 | Apply Filter 41 | Exporting large numbers of members may take some time. Are you sure you want to continue? 42 | Are you sure you want to unlock the selected members? 43 | Are you sure you want to suspend the selected members? 44 | Are you sure you want to approve the selected members? 45 | Suspended 46 | Approved 47 | Locked Out 48 | Unlocked 49 | Yes, approve 50 | Yes, suspend 51 | Yes, unlock 52 | Exportable records 53 | Exporting a large number of members may take some time. 54 | %0% members 55 | Applied Filters 56 | Columns 57 | Format 58 | Select the columns to include in the exported data 59 | 60 | 61 | -------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/lang/sv.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Robert Foster 5 | 6 | 7 | 8 | Manage Members 9 | 10 | 11 | Approved 12 | Locked 13 | Groups 14 | 15 | 16 | Approve 17 | Suspend 18 | Apply Filter 19 | Clear Filter 20 | 21 | 22 | Unlocking %0% Member 23 | Unlocking %0% of %1% Members 24 | Unlocked %0% Member 25 | Unlocked %0% Members 26 | Suspending %0% Member 27 | Suspending %0% of %1% Members 28 | Suspended %0% Member 29 | Suspended %0% Members 30 | Approving %0% Member 31 | Approving %0% of %1% Members 32 | Approved %0% Member 33 | Approved %0% Members 34 | 35 | 36 | Filter 37 | Clear Filter 38 | Export Members 39 | Export 40 | Apply Filter 41 | Exporting large numbers of members may take some time. Are you sure you want to continue? 42 | Are you sure you want to unlock the selected members? 43 | Are you sure you want to suspend the selected members? 44 | Are you sure you want to approve the selected members? 45 | Suspended 46 | Approved 47 | Locked Out 48 | Unlocked 49 | Yes, approve 50 | Yes, suspend 51 | Yes, unlock 52 | Exportable records 53 | Exporting a large number of members may take some time. 54 | %0% members 55 | Applied Filters 56 | Columns 57 | Format 58 | Select the columns to include in the exported data 59 | 60 | 61 | -------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/lang/tr.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Robert Foster 5 | 6 | 7 | 8 | Manage Members 9 | 10 | 11 | Approved 12 | Locked 13 | Groups 14 | 15 | 16 | Approve 17 | Suspend 18 | Apply Filter 19 | Clear Filter 20 | 21 | 22 | Unlocking %0% Member 23 | Unlocking %0% of %1% Members 24 | Unlocked %0% Member 25 | Unlocked %0% Members 26 | Suspending %0% Member 27 | Suspending %0% of %1% Members 28 | Suspended %0% Member 29 | Suspended %0% Members 30 | Approving %0% Member 31 | Approving %0% of %1% Members 32 | Approved %0% Member 33 | Approved %0% Members 34 | 35 | 36 | Filter 37 | Clear Filter 38 | Export Members 39 | Export 40 | Apply Filter 41 | Exporting large numbers of members may take some time. Are you sure you want to continue? 42 | Are you sure you want to unlock the selected members? 43 | Are you sure you want to suspend the selected members? 44 | Are you sure you want to approve the selected members? 45 | Suspended 46 | Approved 47 | Locked Out 48 | Unlocked 49 | Yes, approve 50 | Yes, suspend 51 | Yes, unlock 52 | Exportable records 53 | Exporting a large number of members may take some time. 54 | %0% members 55 | Applied Filters 56 | Columns 57 | Format 58 | Select the columns to include in the exported data 59 | 60 | 61 | -------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/lang/zh-tw.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Robert Foster 5 | 6 | 7 | 8 | Manage Members 9 | 10 | 11 | Approved 12 | Locked 13 | Groups 14 | 15 | 16 | Approve 17 | Suspend 18 | Apply Filter 19 | Clear Filter 20 | 21 | 22 | Unlocking %0% Member 23 | Unlocking %0% of %1% Members 24 | Unlocked %0% Member 25 | Unlocked %0% Members 26 | Suspending %0% Member 27 | Suspending %0% of %1% Members 28 | Suspended %0% Member 29 | Suspended %0% Members 30 | Approving %0% Member 31 | Approving %0% of %1% Members 32 | Approved %0% Member 33 | Approved %0% Members 34 | 35 | 36 | Filter 37 | Clear Filter 38 | Export Members 39 | Export 40 | Apply Filter 41 | Exporting large numbers of members may take some time. Are you sure you want to continue? 42 | Are you sure you want to unlock the selected members? 43 | Are you sure you want to suspend the selected members? 44 | Are you sure you want to approve the selected members? 45 | Suspended 46 | Approved 47 | Locked Out 48 | Unlocked 49 | Yes, approve 50 | Yes, suspend 51 | Yes, unlock 52 | Exportable records 53 | Exporting a large number of members may take some time. 54 | %0% members 55 | Applied Filters 56 | Columns 57 | Format 58 | Select the columns to include in the exported data 59 | 60 | 61 | -------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/lang/zh.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Robert Foster 5 | 6 | 7 | 8 | Manage Members 9 | 10 | 11 | Approved 12 | Locked 13 | Groups 14 | 15 | 16 | Approve 17 | Suspend 18 | Apply Filter 19 | Clear Filter 20 | 21 | 22 | Unlocking %0% Member 23 | Unlocking %0% of %1% Members 24 | Unlocked %0% Member 25 | Unlocked %0% Members 26 | Suspending %0% Member 27 | Suspending %0% of %1% Members 28 | Suspended %0% Member 29 | Suspended %0% Members 30 | Approving %0% Member 31 | Approving %0% of %1% Members 32 | Approved %0% Member 33 | Approved %0% Members 34 | 35 | 36 | Filter 37 | Clear Filter 38 | Export Members 39 | Export 40 | Apply Filter 41 | Exporting large numbers of members may take some time. Are you sure you want to continue? 42 | Are you sure you want to unlock the selected members? 43 | Are you sure you want to suspend the selected members? 44 | Are you sure you want to approve the selected members? 45 | Suspended 46 | Approved 47 | Locked Out 48 | Unlocked 49 | Yes, approve 50 | Yes, suspend 51 | Yes, unlock 52 | Exportable records 53 | Exporting a large number of members may take some time. 54 | %0% members 55 | Applied Filters 56 | Columns 57 | Format 58 | Select the columns to include in the exported data 59 | 60 | 61 | -------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/layouts/list/list.html: -------------------------------------------------------------------------------- 1 | 
3 | 4 |
8 | 9 | 18 | 19 | 20 | 30 | 31 | 32 |
33 | 34 |
35 | 36 | 46 | 47 | 48 | 50 |
No content has been added
51 |
No members have been added
52 |
53 | 54 |
55 | 56 | 58 | 59 | 60 | 61 | 63 | 64 | 65 | 66 |
67 | -------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/layouts/list/list.listviewlayout.controller.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | "use strict"; 3 | 4 | function ListViewListLayoutController($scope, listViewHelper, mediaHelper, mediaTypeHelper, editorService) { 5 | 6 | var vm = this; 7 | var umbracoSettings = Umbraco.Sys.ServerVariables.umbracoSettings; 8 | 9 | vm.nodeId = $scope.contentId; 10 | 11 | // Use whitelist of allowed file types if provided 12 | vm.acceptedFileTypes = mediaHelper.formatFileTypes(umbracoSettings.allowedUploadFiles); 13 | if (vm.acceptedFileTypes === '') { 14 | // If not provided, we pass in a blacklist by adding ! to the file extensions, allowing everything EXCEPT for disallowedUploadFiles 15 | vm.acceptedFileTypes = !mediaHelper.formatFileTypes(umbracoSettings.disallowedUploadFiles); 16 | } 17 | 18 | vm.maxFileSize = umbracoSettings.maxFileSize + "KB"; 19 | vm.activeDrag = false; 20 | vm.isRecycleBin = $scope.contentId === '-21' || $scope.contentId === '-20'; 21 | vm.acceptedMediatypes = []; 22 | 23 | vm.selectItem = selectItem; 24 | vm.clickItem = clickItem; 25 | vm.selectAll = selectAll; 26 | vm.isSelectedAll = isSelectedAll; 27 | vm.isSortDirection = isSortDirection; 28 | vm.sort = sort; 29 | vm.dragEnter = dragEnter; 30 | vm.dragLeave = dragLeave; 31 | vm.onFilesQueue = onFilesQueue; 32 | vm.onUploadComplete = onUploadComplete; 33 | markAsSensitive(); 34 | 35 | function activate() { 36 | if ($scope.entityType === 'media') { 37 | mediaTypeHelper.getAllowedImagetypes(vm.nodeId).then(function (types) { 38 | vm.acceptedMediatypes = types; 39 | }); 40 | } 41 | } 42 | 43 | function selectAll() { 44 | listViewHelper.selectAllItemsToggle($scope.items, $scope.selection); 45 | } 46 | 47 | function isSelectedAll() { 48 | return listViewHelper.isSelectedAll($scope.items, $scope.selection); 49 | } 50 | 51 | function selectItem(selectedItem, $index, $event) { 52 | listViewHelper.selectHandler(selectedItem, $index, $scope.items, $scope.selection, $event); 53 | } 54 | 55 | function clickItem(item) { 56 | var memberEditor = { 57 | id: item.key, 58 | view: "views/member/edit.html", 59 | submit: function (model) { 60 | $scope.getContent($scope.contentId) 61 | editorService.close() 62 | }, 63 | close: function () { 64 | editorService.close() 65 | } 66 | } 67 | editorService.open(memberEditor) 68 | } 69 | 70 | function isSortDirection(col, direction) { 71 | return listViewHelper.setSortingDirection(col, direction, $scope.options); 72 | } 73 | 74 | function sort(field, allow, isSystem) { 75 | if (allow) { 76 | $scope.options.orderBySystemField = isSystem; 77 | listViewHelper.setSorting(field, allow, $scope.options); 78 | $scope.getContent($scope.contentId); 79 | } 80 | } 81 | 82 | // Dropzone upload functions 83 | function dragEnter(el, event) { 84 | vm.activeDrag = true; 85 | } 86 | 87 | function dragLeave(el, event) { 88 | vm.activeDrag = false; 89 | } 90 | 91 | function onFilesQueue() { 92 | vm.activeDrag = false; 93 | } 94 | 95 | function onUploadComplete() { 96 | $scope.getContent($scope.contentId); 97 | } 98 | 99 | function markAsSensitive() { 100 | angular.forEach($scope.options.includeProperties, function (option) { 101 | option.isSensitive = false; 102 | 103 | angular.forEach($scope.items, 104 | function (item) { 105 | 106 | angular.forEach(item.properties, 107 | function (property) { 108 | 109 | if (option.alias === property.alias) { 110 | option.isSensitive = property.isSensitive; 111 | } 112 | 113 | }); 114 | 115 | }); 116 | 117 | }); 118 | } 119 | 120 | activate(); 121 | 122 | } 123 | 124 | angular.module("umbraco").controller("MemberListView.PropertyEditors.ListView.ListLayoutController", ListViewListLayoutController); 125 | 126 | })(); 127 | -------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/overlays/action.controller.js: -------------------------------------------------------------------------------- 1 | function actionController($scope, localizationService) { 2 | "use strict"; 3 | 4 | localizationService.localize($scope.model.actionKey).then(function (value) { 5 | $scope.question = value 6 | }); 7 | } 8 | 9 | angular.module("umbraco").controller("MemberListView.Overlays.ActionController", actionController); -------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/overlays/action.html: -------------------------------------------------------------------------------- 1 | 
2 | 3 |

4 | {{question}}? 5 |

6 | 7 |
8 | -------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/package.manifest: -------------------------------------------------------------------------------- 1 | { 2 | "dashboards": [ 3 | { 4 | "alias": "memberManager", 5 | "view": "/App_Plugins/MemberListView/dashboard/memberManager.html", 6 | "sections": [ "member" ], 7 | "weight": -10 8 | } 9 | ], 10 | "javascript": [ 11 | "~/App_Plugins/MemberListView/resources/member.resource.js", 12 | "~/App_Plugins/MemberListView/overlays/action.controller.js", 13 | "~/App_Plugins/MemberListView/layouts/list/list.listviewlayout.controller.js", 14 | "~/App_Plugins/MemberListView/dashboard/memberManager.controller.js", 15 | "~/App_Plugins/MemberListView/dialogs/member/filter.controller.js", 16 | "~/App_Plugins/MemberListView/dialogs/member/export.controller.js" 17 | ], 18 | "css": [ 19 | "~/App_Plugins/MemberListView/css/memberListView.css" 20 | ] 21 | } -------------------------------------------------------------------------------- /MemberListView/App_Plugins/MemberListView/scss/memberListView.scss: -------------------------------------------------------------------------------- 1 | .umb-listview.membermanager { 2 | a { 3 | text-decoration: none; 4 | } 5 | 6 | .umb-table__name { 7 | -ms-flex: 1 1 5%; 8 | flex: 1 1 5%; 9 | max-width: 20%; 10 | } 11 | 12 | .no-results { 13 | padding: 20px; 14 | background-color: #f8f8f8; 15 | border: 1px solid #dbdbdb; 16 | text-align: center; 17 | color: #999; 18 | margin-top: 20px; 19 | } 20 | 21 | .umb-table-cell > * { 22 | overflow: visible; 23 | white-space: unset; 24 | } 25 | } 26 | 27 | .filter-display { 28 | padding: 1em; 29 | background-color: #f8f8f8; 30 | border: 1px solid #dbdbdb; 31 | margin-bottom: 1em; 32 | 33 | > div { 34 | overflow: auto; 35 | margin-bottom: 5px; 36 | 37 | &:last-child { 38 | margin-bottom: 0; 39 | } 40 | 41 | strong { 42 | float: left; 43 | width: 180px; 44 | display: block; 45 | } 46 | 47 | span { 48 | float: left; 49 | width: auto; 50 | } 51 | } 52 | } 53 | 54 | .control-flow-horizontal { 55 | .control-label { 56 | float: none; 57 | } 58 | 59 | .controls.controls-row { 60 | margin-left: 0px; 61 | } 62 | } 63 | 64 | .filter-options { 65 | 66 | .filter-display { 67 | margin-bottom: 0; 68 | } 69 | 70 | .filter-checkbox-list { 71 | margin-bottom: 1em; 72 | 73 | .filter-content { 74 | max-height: 10.4em; 75 | overflow-y: scroll; 76 | border: solid 1px #eee; 77 | border-radius: .25em; 78 | } 79 | 80 | .filter-content, .filter-header { 81 | padding: .5em; 82 | 83 | > div { 84 | overflow: auto; 85 | 86 | input[type="checkbox"] { 87 | float: left; 88 | /*margin-top: 10px;*/ 89 | margin-right: 10px; 90 | } 91 | 92 | label.control-label { 93 | float: left; 94 | margin: 0; 95 | padding: 0; 96 | width: auto; 97 | width: 90%; 98 | font-weight: 400; 99 | } 100 | } 101 | } 102 | 103 | .filter-header { 104 | margin-bottom: .5em; 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /MemberListView/Composing/MemberListViewComposer.cs: -------------------------------------------------------------------------------- 1 | using MemberListView.Indexing; 2 | using MemberListView.Models.Mapping; 3 | using MemberListView.Services; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Umbraco.Cms.Core.Composing; 6 | using Umbraco.Cms.Core.DependencyInjection; 7 | using Umbraco.Cms.Core.Notifications; 8 | using Umbraco.Extensions; 9 | 10 | namespace MemberListView.Composing 11 | { 12 | public class MemberListViewComposer : IComposer 13 | { 14 | public MemberListViewComposer() 15 | { 16 | } 17 | public void Compose(IUmbracoBuilder builder) 18 | { 19 | builder.Services.AddOptions() 20 | .Configure(builder.Config.GetSection(nameof(Config.MemberListView))); 21 | 22 | // Extend the Member Index fieldset. 23 | builder.Services.ConfigureOptions(); 24 | 25 | builder.MapDefinitions().Add(); 26 | 27 | builder.Components().Append(); 28 | builder.AddNotificationHandler(); 29 | 30 | builder.Services.AddUnique(); 31 | 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /MemberListView/Composing/PackageManifestComposer.cs: -------------------------------------------------------------------------------- 1 | using Umbraco.Cms.Core.Composing; 2 | using Umbraco.Cms.Core.DependencyInjection; 3 | using Umbraco.Cms.Core.Manifest; 4 | 5 | namespace MemberListView.Composing 6 | { 7 | public class PackageManifestComposer : IComposer 8 | { 9 | public void Compose(IUmbracoBuilder builder) 10 | { 11 | builder.ManifestFilters().Append(); 12 | } 13 | } 14 | 15 | public class MemberListViewManifestFilter : IManifestFilter 16 | { 17 | public void Filter(List manifests) 18 | { 19 | var version = typeof(MemberListViewManifestFilter).Assembly.GetName().Version?.ToString(); 20 | 21 | if (version is not null) 22 | { 23 | manifests.Add(new PackageManifest 24 | { 25 | PackageName = "MemberListView", 26 | AllowPackageTelemetry = true, 27 | Version = version 28 | }); 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /MemberListView/Composing/ServerVariablesParsingHandler.cs: -------------------------------------------------------------------------------- 1 | using MemberListView.Controllers; 2 | using Microsoft.AspNetCore.Routing; 3 | using Umbraco.Cms.Core.Events; 4 | using Umbraco.Cms.Core.Notifications; 5 | using Umbraco.Extensions; 6 | 7 | namespace MemberListView.Composing 8 | { 9 | internal class ServerVariablesParsingHandler : 10 | INotificationHandler 11 | { 12 | private readonly LinkGenerator linkGenerator; 13 | 14 | public ServerVariablesParsingHandler(LinkGenerator linkGenerator) 15 | { 16 | this.linkGenerator = linkGenerator; 17 | } 18 | 19 | public void Handle(ServerVariablesParsingNotification notification) 20 | { 21 | IDictionary serverVars = notification.ServerVariables; 22 | 23 | if (!serverVars.ContainsKey("umbracoUrls")) 24 | { 25 | throw new ArgumentException("Missing umbracoUrls."); 26 | } 27 | 28 | var umbracoUrlsObject = serverVars["umbracoUrls"]; 29 | if (umbracoUrlsObject == null) 30 | { 31 | throw new ArgumentException("Null umbracoUrls"); 32 | } 33 | 34 | if (!(umbracoUrlsObject is Dictionary umbracoUrls)) 35 | { 36 | throw new ArgumentException("Invalid umbracoUrls"); 37 | } 38 | 39 | if (!serverVars.ContainsKey("umbracoPlugins")) 40 | { 41 | throw new ArgumentException("Missing umbracoPlugins."); 42 | } 43 | 44 | if (!(serverVars["umbracoPlugins"] is Dictionary umbracoPlugins)) 45 | { 46 | throw new ArgumentException("Invalid umbracoPlugins"); 47 | } 48 | 49 | var memberListViewBaseUrl = linkGenerator.GetUmbracoApiServiceBaseUrl(controller => 50 | controller.GetCanExport()); 51 | 52 | if (memberListViewBaseUrl is not null && !umbracoUrls.ContainsKey(nameof(memberListViewBaseUrl))) 53 | { 54 | umbracoUrls[nameof(memberListViewBaseUrl)] = memberListViewBaseUrl; 55 | } 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /MemberListView/Config/MemberListView.cs: -------------------------------------------------------------------------------- 1 | namespace MemberListView.Config 2 | { 3 | public class MemberListView 4 | { 5 | internal string[]? ExportExcludedColumns { get; private set; } 6 | 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /MemberListView/Config/memberListView.json: -------------------------------------------------------------------------------- 1 | { 2 | "columns": [ 3 | { 4 | "alias": "firstName", 5 | "header": "First Name", 6 | "isSystem": 0, 7 | "position": 1 8 | }, 9 | { 10 | "alias": "lastName", 11 | "header": "Last Name", 12 | "isSystem": 0, 13 | "position": 2 14 | }, 15 | { 16 | "alias": "phoneNumber", 17 | "header": "Phone", 18 | "isSystem": 0, 19 | "position": 3 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /MemberListView/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace MemberListView 2 | { 3 | internal static class Constants 4 | { 5 | internal const string PluginName = "MemberListView"; 6 | 7 | //internal static class Dashboards 8 | //{ 9 | // internal const string MemberManager = "memberManager"; 10 | //} 11 | 12 | internal static class PropertyEditors 13 | { 14 | internal const string MemberListView = "memberListView"; 15 | } 16 | internal static class Indexing 17 | { 18 | internal const string Comments = "comments"; 19 | internal const string FailedPasswordAttempts = "failedPasswordAttempts"; 20 | internal const string LastLoginDate = "lastLoginDate"; 21 | internal const string LastLockoutDate = "lastLockoutDate"; 22 | internal const string LastPasswordChangeDate = "lastPasswordChangeDate"; 23 | internal const string IsLockedOut = "isLockedOut"; 24 | internal const string IsApproved = "isApproved"; 25 | } 26 | 27 | internal static class Configuration 28 | { 29 | internal const string SectionName = "MemberListView"; 30 | internal const string ExportExcludedColumns = "ExportExcludedColumns"; 31 | } 32 | 33 | internal static class Members 34 | { 35 | internal const string Ids = "ids"; 36 | internal const string Groups = "memberGroups"; 37 | internal const string MemberType = "memberType"; 38 | internal const string MemberApproved = "umbracoMemberApproved"; 39 | internal const string MemberLockedOut = "umbracoMemberLockedOut"; 40 | } 41 | 42 | internal static class MimeTypes 43 | { 44 | internal const string Excel = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; 45 | internal const string CSV = "application/octet-stream"; 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /MemberListView/Extensions/HttpRequestMessageExtensions.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Http; 2 | 3 | namespace MemberListView.Extensions; 4 | 5 | internal static partial class HttpRequestMessageExtensions 6 | { 7 | internal static IEnumerable? GetColumns(this HttpRequest request) 8 | { 9 | return request.Query["columns"] 10 | .FirstOrDefault() 11 | ?.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); 12 | } 13 | 14 | 15 | internal static string? GetMemberType(this HttpRequest request) 16 | { 17 | return request.Query[Constants.Members.MemberType].FirstOrDefault(); 18 | } 19 | 20 | internal static bool? GetIsLockedOut(this HttpRequest request) 21 | { 22 | return int.TryParse(request.Query[Constants.Members.MemberLockedOut].FirstOrDefault(), out int boolValue) ? boolValue == 1 : null; 23 | } 24 | 25 | internal static bool? GetIsApproved(this HttpRequest request) 26 | { 27 | return int.TryParse(request.Query[Constants.Members.MemberApproved].FirstOrDefault(), out int boolValue) ? boolValue == 1 : null; 28 | } 29 | 30 | internal static IEnumerable GetGroups(this HttpRequest request) 31 | { 32 | var groups = request.Query[Constants.Members.Groups].FirstOrDefault(); 33 | if (groups is null) 34 | { 35 | yield break; 36 | } 37 | foreach (var id in groups.Split(new[] { ',' })) 38 | { 39 | if (int.TryParse(id, out int groupId)) 40 | { 41 | yield return groupId; 42 | } 43 | } 44 | } 45 | 46 | internal static IEnumerable GetMemberIds(this HttpRequest request) 47 | { 48 | 49 | var ids = request.Query[Constants.Members.Ids].FirstOrDefault(); 50 | if (ids is null) 51 | { 52 | yield break; 53 | } 54 | foreach (var id in ids.Split(new[] { ',' })) 55 | { 56 | yield return id; 57 | } 58 | } 59 | 60 | internal static Dictionary GetFilters(this HttpRequest request) 61 | { 62 | 63 | Dictionary filters = new(); 64 | 65 | foreach (var kvp in request.Query 66 | .Where(q => (q.Key.StartsWith("f_") || q.Key == Constants.Members.Groups) && !string.IsNullOrWhiteSpace(q.Value))) 67 | { 68 | filters.Add(kvp.Key, kvp.Value!); 69 | } 70 | 71 | return filters; 72 | } 73 | } -------------------------------------------------------------------------------- /MemberListView/Extensions/SearchExtensions.cs: -------------------------------------------------------------------------------- 1 | using Examine; 2 | using Examine.Search; 3 | using static Umbraco.Cms.Core.Constants; 4 | 5 | namespace MemberListView.Extensions; 6 | 7 | internal static class SearchExtensions 8 | { 9 | 10 | internal static IQuery And(this IQuery query, IBooleanOperation? op) 11 | { 12 | return op?.And() ?? query; 13 | } 14 | 15 | internal static IQuery Or(this IQuery query, IBooleanOperation? op) 16 | { 17 | return op?.Or() ?? query; 18 | } 19 | 20 | internal static IQuery Not(this IQuery query, IBooleanOperation? op) 21 | { 22 | return op?.Not() ?? query; 23 | } 24 | 25 | internal static IBooleanOperation BooleanField(this IQuery query, string field, bool value) 26 | { 27 | return query.GroupedOr(new[] { field }, value.ToString(), value ? "1" : "0"); 28 | } 29 | 30 | internal static IQuery InitialiseQuery(this IExamineManager examineManager, string indexName = UmbracoIndexes.ExternalIndexName, string? category = null, BooleanOperation operation = BooleanOperation.And) 31 | { 32 | if (examineManager.TryGetIndex(indexName, out var index)) 33 | { 34 | var searcher = index.Searcher; 35 | return searcher.CreateQuery(category, defaultOperation: operation); 36 | } 37 | throw new ApplicationException("The search provider is not configured correctly."); 38 | } 39 | 40 | internal static List GetObjList(this object obj) 41 | { 42 | return new List { obj }; 43 | } 44 | } -------------------------------------------------------------------------------- /MemberListView/Indexing/ConfigureMemberIndex.cs: -------------------------------------------------------------------------------- 1 | using Examine; 2 | using Examine.Lucene; 3 | using Microsoft.Extensions.Options; 4 | using static Umbraco.Cms.Core.Constants; 5 | 6 | namespace MemberListView.Indexing; 7 | 8 | // See https://shazwazza.github.io/Examine/configuration for details 9 | internal class ConfigureMemberIndexOptions : IConfigureNamedOptions 10 | { 11 | 12 | public void Configure(string? name, LuceneDirectoryIndexOptions options) 13 | { 14 | switch (name) 15 | { 16 | case UmbracoIndexes.MembersIndexName: 17 | options.FieldDefinitions.AddOrUpdate(new FieldDefinition(Constants.Indexing.Comments, FieldDefinitionTypes.FullText)); 18 | 19 | options.FieldDefinitions.AddOrUpdate(new FieldDefinition(Constants.Indexing.IsLockedOut, FieldDefinitionTypes.FullTextSortable)); 20 | options.FieldDefinitions.AddOrUpdate(new FieldDefinition(Constants.Indexing.IsApproved, FieldDefinitionTypes.FullTextSortable)); 21 | options.FieldDefinitions.AddOrUpdate(new FieldDefinition(Constants.Indexing.FailedPasswordAttempts, FieldDefinitionTypes.Integer)); 22 | 23 | options.FieldDefinitions.AddOrUpdate(new FieldDefinition(Constants.Indexing.LastLoginDate, FieldDefinitionTypes.DateTime)); 24 | options.FieldDefinitions.AddOrUpdate(new FieldDefinition(Constants.Indexing.LastLockoutDate, FieldDefinitionTypes.DateTime)); 25 | options.FieldDefinitions.AddOrUpdate(new FieldDefinition(Constants.Indexing.LastPasswordChangeDate, FieldDefinitionTypes.DateTime)); 26 | 27 | options.FieldDefinitions.AddOrUpdate(new FieldDefinition(Constants.Members.Groups, FieldDefinitionTypes.FullTextSortable)); 28 | 29 | break; 30 | } 31 | } 32 | 33 | public void Configure(LuceneDirectoryIndexOptions options) 34 | => Configure(null, options); 35 | } -------------------------------------------------------------------------------- /MemberListView/Indexing/MemberIndexingComponent.cs: -------------------------------------------------------------------------------- 1 | using Examine; 2 | using Examine.Lucene.Providers; 3 | using MemberListView.Extensions; 4 | using Microsoft.Extensions.Logging; 5 | using Umbraco.Cms.Core.Composing; 6 | using Umbraco.Cms.Core.Models; 7 | using Umbraco.Cms.Core.PropertyEditors; 8 | using Umbraco.Cms.Core.Services; 9 | using Umbraco.Extensions; 10 | using static Umbraco.Cms.Core.Constants; 11 | 12 | namespace MemberListView.Indexing 13 | { 14 | public class MemberIndexingComponent : IComponent 15 | { 16 | private readonly IExamineManager examineManager; 17 | private readonly ILogger logger; 18 | private readonly IMemberTypeService memberTypeService; 19 | private readonly IMemberService memberService; 20 | private readonly PropertyEditorCollection propertyEditors; 21 | 22 | public MemberIndexingComponent(IExamineManager examineManager, 23 | ILogger logger, 24 | IMemberTypeService memberTypeService, 25 | IMemberService memberService, 26 | PropertyEditorCollection propertyEditors) 27 | { 28 | this.examineManager = examineManager; 29 | this.logger = logger; 30 | this.memberTypeService = memberTypeService; 31 | this.memberService = memberService; 32 | this.propertyEditors = propertyEditors; 33 | } 34 | 35 | public void Initialize() 36 | { 37 | SetupIndexTransformation(UmbracoIndexes.MembersIndexName); 38 | } 39 | 40 | private void SetupIndexTransformation(string indexName) 41 | { 42 | if (!examineManager.TryGetIndex(indexName, out IIndex index)) 43 | { 44 | logger.LogWarning("No index found by the name {indexName}", indexName); 45 | return; 46 | } 47 | 48 | if (index is LuceneIndex luceneIndex) 49 | { 50 | luceneIndex.TransformingIndexValues += LuceneIndex_TransformingIndexValues; 51 | luceneIndex.IndexingError += LuceneIndex_IndexingError; 52 | } 53 | } 54 | 55 | private void LuceneIndex_IndexingError(object? sender, IndexingErrorEventArgs e) 56 | { 57 | logger.LogWarning(e.Exception, "Error occurred during indexing: {message}", e.Message); 58 | } 59 | 60 | private void LuceneIndex_TransformingIndexValues(object? sender, IndexingItemEventArgs e) 61 | { 62 | var memberTypes = memberTypeService.GetAll().Select(x => x.Alias); 63 | 64 | if (!memberTypes.InvariantContains(e.ValueSet.ItemType)) 65 | { 66 | return; 67 | } 68 | 69 | var updatedValues = e.ValueSet.Values.ToDictionary(x => x.Key, x => x.Value.ToList()); 70 | 71 | // Get the groups 72 | AddGroups(e, updatedValues); 73 | var member = memberService.GetById(int.Parse(e.ValueSet.Id)); 74 | if (member is not null) 75 | { 76 | IndexMembershipFields(e, updatedValues, member); 77 | } 78 | e.SetValues(updatedValues.ToDictionary(x => x.Key, x => (IEnumerable)x.Value)); 79 | } 80 | 81 | private void AddGroups(IndexingItemEventArgs e, Dictionary> updatedValues) 82 | { 83 | var groups = memberService.GetAllRoles(int.Parse(e.ValueSet.Id)).Aggregate("", (list, group) => string.IsNullOrEmpty(list) ? group : $"{list} {group}"); 84 | updatedValues.Add(Constants.Members.Groups, groups.GetObjList()); 85 | } 86 | 87 | /// 88 | /// Make sure the isApproved and isLockedOut fields are setup properly in the index 89 | /// 90 | /// 91 | /// 92 | /// 93 | /// 94 | /// these fields are not consistently updated in the XML fragment when a member is saved (as they may never get set) so we have to do this. 95 | /// 96 | private void IndexMembershipFields(IndexingItemEventArgs e, Dictionary> updatedValues, IMember member) 97 | { 98 | if (!e.ValueSet.Values.ContainsKey(nameof(IMember.IsLockedOut))) 99 | { 100 | updatedValues.Add(nameof(IMember.IsLockedOut), member.IsLockedOut.ToString().GetObjList()); 101 | } 102 | 103 | if (!e.ValueSet.Values.ContainsKey(nameof(IMember.IsApproved))) 104 | { 105 | updatedValues.Add(nameof(IMember.IsApproved), member.IsApproved.ToString().GetObjList()); 106 | } 107 | 108 | if (!e.ValueSet.Values.ContainsKey(nameof(IMember.FailedPasswordAttempts))) 109 | { 110 | updatedValues.Add(nameof(IMember.FailedPasswordAttempts), member.FailedPasswordAttempts.GetObjList()); 111 | } 112 | 113 | if (!e.ValueSet.Values.ContainsKey(nameof(IMember.LastLoginDate)) && member.LastLoginDate is not null) 114 | { 115 | updatedValues.Add(nameof(IMember.LastLoginDate), member.LastLoginDate.GetObjList()); 116 | } 117 | 118 | if (!e.ValueSet.Values.ContainsKey(nameof(IMember.LastLockoutDate)) && member.LastLockoutDate is not null) 119 | { 120 | updatedValues.Add(nameof(IMember.LastLockoutDate), member.LastLockoutDate.GetObjList()); 121 | } 122 | 123 | if (!e.ValueSet.Values.ContainsKey(nameof(IMember.LastPasswordChangeDate)) && member.LastPasswordChangeDate is not null) 124 | { 125 | updatedValues.Add(nameof(IMember.LastPasswordChangeDate), member.LastPasswordChangeDate.GetObjList()); 126 | } 127 | 128 | var values = new Dictionary>(); 129 | foreach (var property in member.Properties) 130 | { 131 | AddPropertyValue(property, null, null, values); 132 | } 133 | 134 | foreach (var value in values) 135 | { 136 | if (e.ValueSet.Values.ContainsKey(value.Key)) 137 | { 138 | continue; 139 | } 140 | var val = value.Value.FirstOrDefault(); 141 | if (val != null) 142 | { 143 | updatedValues.Add(value.Key, val.GetObjList()); 144 | } 145 | } 146 | } 147 | 148 | protected void AddPropertyValue(IProperty property, string? culture, string? segment, IDictionary> values) 149 | { 150 | var editor = propertyEditors[property.PropertyType.PropertyEditorAlias]; 151 | if (editor == null) return; 152 | 153 | var indexVals = editor.PropertyIndexValueFactory.GetIndexValues(property, culture, segment, false); 154 | foreach (var keyVal in indexVals) 155 | { 156 | if (keyVal.Key.IsNullOrWhiteSpace()) continue; 157 | 158 | var cultureSuffix = culture == null ? string.Empty : $"_{culture}"; 159 | 160 | foreach (var val in keyVal.Value) 161 | { 162 | switch (val) 163 | { 164 | //only add the value if its not null or empty (we'll check for string explicitly here too) 165 | case null: 166 | continue; 167 | case string strVal: 168 | { 169 | if (strVal.IsNullOrWhiteSpace()) continue; 170 | var key = $"{keyVal.Key}{cultureSuffix}"; 171 | if (values.TryGetValue(key, out _)) 172 | values[key] = val.Yield(); 173 | else 174 | values.Add($"{keyVal.Key}{cultureSuffix}", val.Yield()); 175 | } 176 | break; 177 | default: 178 | { 179 | var key = $"{keyVal.Key}{cultureSuffix}"; 180 | if (values.TryGetValue(key, out _)) 181 | values[key] = val.Yield(); 182 | else 183 | values.Add($"{keyVal.Key}{cultureSuffix}", val.Yield()); 184 | } 185 | 186 | break; 187 | } 188 | } 189 | } 190 | } 191 | 192 | public void Terminate() 193 | { 194 | } 195 | } 196 | } -------------------------------------------------------------------------------- /MemberListView/MemberExamineIndexFieldNames.cs: -------------------------------------------------------------------------------- 1 | namespace MemberListView 2 | { 3 | internal static class MemberExamineIndexFieldNames 4 | { 5 | internal const string Id = "id"; 6 | internal const string Email = "email"; 7 | internal const string LoginName = "loginName"; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /MemberListView/MemberListView.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0;net7.0 5 | enable 6 | enable 7 | true 8 | $(NoWarn);1591 9 | 10 | true 11 | . 12 | 13 | 3.0.4 14 | $(MSBuildProjectName) 15 | $(MSBuildProjectName.Replace(" ", "_")) 16 | Copyright © Robert Foster 17 | Adds a MemberListView dashboard to the Members area in Umbraco 10+ to allow 18 | easier management of members including approval and unlocking capabilities. 19 | 20 | 21 | Umbraco Membership Member Management umbraco-marketplace 22 | Membership_logo.png 23 | https://raw.githubusercontent.com/YourITGroup/umbMemberListView/master/assets/Membership_logo.png 24 | https://github.com/YourITGroup/umbMemberListView 25 | Your IT Group Pty Ltd 26 | Robert Foster 27 | true 28 | https://github.com/YourITGroup/umbMemberListView 29 | git 30 | True 31 | Apache-2.0 32 | README.nuget.md 33 | $(AssemblyName) for Umbraco10+ 34 | 35 | 36 | 37 | 38 | 39 | 40 | true 41 | 42 | 43 | true 44 | snupkg 45 | 46 | 47 | 48 | AnyCPU 49 | 50 | 51 | AnyCPU 52 | 53 | 54 | 55 | AnyCPU 56 | 57 | 58 | 59 | 60 | AnyCPU 61 | 62 | 63 | 64 | true 65 | 66 | 67 | 68 | 69 | True 70 | 71 | 72 | 73 | True 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 10.0.0 88 | 89 | 90 | 10.0.0 91 | 92 | 93 | 94 | 95 | 96 | 11.0.0 97 | 98 | 99 | 11.0.0 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | compilerconfig.json 112 | 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /MemberListView/Models/ExportFormat.cs: -------------------------------------------------------------------------------- 1 | namespace MemberListView.Models 2 | { 3 | public enum ExportFormat 4 | { 5 | CSV = 0, 6 | Excel 7 | } 8 | } -------------------------------------------------------------------------------- /MemberListView/Models/Mapping/MemberListItemMapDefinition.cs: -------------------------------------------------------------------------------- 1 | using Umbraco.Cms.Core; 2 | using Umbraco.Cms.Core.Mapping; 3 | using Umbraco.Cms.Core.Models; 4 | using Umbraco.Cms.Core.Models.ContentEditing; 5 | using Umbraco.Cms.Core.Models.Membership; 6 | using Umbraco.Cms.Core.Services; 7 | using Umbraco.Extensions; 8 | using static Umbraco.Cms.Core.Constants; 9 | using UserProfile = Umbraco.Cms.Core.Models.ContentEditing.UserProfile; 10 | 11 | namespace MemberListView.Models.Mapping; 12 | 13 | internal class MemberListItemMapDefinition : IMapDefinition 14 | { 15 | private readonly IUserService userService; 16 | private readonly IMemberTypeService memberTypeService; 17 | private readonly IMemberService memberService; 18 | 19 | public MemberListItemMapDefinition(IUserService userService, IMemberTypeService memberTypeService, IMemberService memberService) 20 | { 21 | this.userService = userService; 22 | this.memberTypeService = memberTypeService; 23 | this.memberService = memberService; 24 | } 25 | 26 | public void DefineMaps(IUmbracoMapper mapper) 27 | => mapper.Define( 28 | (source, context) => new MemberListItem(), 29 | Map 30 | ); 31 | 32 | private UserProfile? GetOwner(IContentBase source, MapperContext context) 33 | { 34 | var profile = source.GetCreatorProfile(userService); 35 | return profile == null ? null : context.Map(profile); 36 | } 37 | 38 | private IEnumerable? GetMemberGroups(string username) 39 | { 40 | var userRoles = username.IsNullOrWhiteSpace() ? null : memberService.GetAllRoles(username); 41 | 42 | return userRoles; 43 | } 44 | 45 | private void Map(IMember source, MemberListItem target, MapperContext context) 46 | { 47 | target.ContentTypeId = source.ContentType.Id; 48 | target.ContentTypeAlias = source.ContentType.Alias; 49 | target.CreateDate = source.CreateDate; 50 | target.Email = source.Email; 51 | target.Icon = source.ContentType.Icon; 52 | target.Id = int.MaxValue; 53 | target.Key = source.Key; 54 | target.Name = source.Name; 55 | target.Owner = GetOwner(source, context); 56 | target.ParentId = source.ParentId; 57 | target.Path = source.Path; 58 | var properties = source.Properties.ToArray(); 59 | target.Properties = context.MapEnumerable(properties); 60 | target.SortOrder = source.SortOrder; 61 | target.State = null; 62 | target.Udi = Udi.Create(UdiEntityType.Member, source.Key); 63 | target.UpdateDate = source.UpdateDate; 64 | target.Username = source.Username; 65 | 66 | target.MemberGroups = GetMemberGroups(source.Username); 67 | target.IsLockedOut = source.IsLockedOut; 68 | target.IsApproved = source.IsApproved; 69 | target.ContentType = memberTypeService.Get(source.ContentType.Alias); 70 | } 71 | } -------------------------------------------------------------------------------- /MemberListView/Models/MemberColumn.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | 3 | namespace MemberListView.Models 4 | { 5 | [DataContract] 6 | public class MemberColumn 7 | { 8 | [DataMember(Name = "id")] 9 | public string? Id { get; set; } 10 | 11 | [DataMember(Name = "alias")] 12 | public string? Alias { get; set; } 13 | 14 | [DataMember(Name = "name")] 15 | public string? Name { get; set; } 16 | } 17 | } -------------------------------------------------------------------------------- /MemberListView/Models/MemberListItem.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.Serialization; 2 | using Umbraco.Cms.Core.Models; 3 | using Umbraco.Cms.Core.Models.ContentEditing; 4 | 5 | namespace MemberListView.Models; 6 | 7 | public class MemberListItem : MemberBasic 8 | { 9 | [DataMember(Name = "isApproved")] 10 | public bool IsApproved { get; set; } 11 | 12 | [DataMember(Name = "isLockedOut")] 13 | public bool IsLockedOut { get; set; } 14 | 15 | [DataMember(Name = "memberType")] 16 | public IMemberType? ContentType { get; set; } 17 | 18 | [DataMember(Name = "memberGroups")] 19 | public IEnumerable? MemberGroups { get; set; } 20 | 21 | } -------------------------------------------------------------------------------- /MemberListView/Services/IMemberExtendedService.cs: -------------------------------------------------------------------------------- 1 | #if NET5_0_OR_GREATER 2 | using Umbraco.Cms.Core; 3 | using Umbraco.Cms.Core.Models; 4 | using Umbraco.Cms.Core.Models.Membership; 5 | using Umbraco.Cms.Core.Services; 6 | #else 7 | using Umbraco.Core.Models; 8 | using Umbraco.Core.Persistence.DatabaseModelDefinitions; 9 | using Umbraco.Core.Services; 10 | #endif 11 | 12 | namespace MemberListView.Services 13 | { 14 | public interface IMemberExtendedService : IMemberService 15 | { 16 | /// 17 | /// Gets a list of paged objects matching the criteria 18 | /// 19 | /// An can be of type 20 | /// Current page index 21 | /// Size of the page 22 | /// Total number of records found (out) 23 | /// Field to order by 24 | /// Direction to order by 25 | /// Flag to indicate when ordering by system field 26 | /// 27 | /// Search text filter 28 | /// 29 | /// 30 | /// additional filter conditions 31 | /// Optional filter on IsApproved state 32 | /// Optional filter on IsLockedOut state 33 | /// 34 | IEnumerable GetPage(long pageIndex, int pageSize, out long totalRecords, string orderBy, 35 | Direction orderDirection, bool orderBySystemField, string? memberTypeAlias, 36 | string filter = "", IEnumerable? ids = null, 37 | IEnumerable? groups = null, 38 | IDictionary? additionalFilters = null, bool? isApproved = null, 39 | bool? isLockedOut = null); 40 | 41 | /// 42 | /// Gets all objects matching the criteria 43 | /// 44 | /// Field to order by 45 | /// Direction to order by 46 | /// Flag to indicate when ordering by system field 47 | /// 48 | /// Search text filter 49 | /// 50 | /// 51 | /// List of columns to include in the export 52 | /// additional filter conditions 53 | /// Optional filter on IsApproved state 54 | /// Optional filter on IsLockedOut state 55 | /// 56 | IEnumerable GetForExport(string orderBy, Direction orderDirection, bool orderBySystemField, 57 | string? memberTypeAlias, string filter = "", 58 | IEnumerable? ids = null, IEnumerable? groups = null, 59 | IEnumerable? includedColumns = null, 60 | IDictionary? additionalFilters = null, 61 | bool? isApproved = null, bool? isLockedOut = null); 62 | 63 | } 64 | } -------------------------------------------------------------------------------- /MemberListView/Utility/MemberDataUdiParser.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using Umbraco.Cms.Core; 3 | namespace MemberListView.Utility 4 | { 5 | internal sealed class MemberDataUdiParser 6 | { 7 | public MemberDataUdiParser() 8 | { 9 | } 10 | 11 | private static readonly Regex UdiRegex = new Regex(@"(?umb://[A-z0-9\-]+/[A-z0-9]+)", 12 | RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); 13 | 14 | /// 15 | /// Parses out media UDIs from an html string based on 'data-udi' html attributes 16 | /// 17 | /// 18 | /// 19 | public static IEnumerable FindUdis(string text) 20 | { 21 | var matches = UdiRegex.Matches(text); 22 | if (matches.Count == 0) 23 | yield break; 24 | 25 | foreach (Match match in matches) 26 | { 27 | if (match.Groups.Count == 2 && UdiParser.TryParse(match.Groups[1].Value, out var udi)) 28 | yield return udi; 29 | } 30 | } 31 | 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /MemberListView/build/MemberListView.targets: -------------------------------------------------------------------------------- 1 |  2 | 3 | $(MSBuildThisFileDirectory)..\App_Plugins\MemberListView\**\*.* 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /MemberListView/compilerconfig.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "outputFile": "App_Plugins/MemberManager/css/memberListView.css", 4 | "inputFile": "App_Plugins/MemberManager/scss/memberListView.scss" 5 | } 6 | ] -------------------------------------------------------------------------------- /MemberListView/compilerconfig.json.defaults: -------------------------------------------------------------------------------- 1 | { 2 | "compilers": { 3 | "less": { 4 | "autoPrefix": "", 5 | "cssComb": "none", 6 | "ieCompat": true, 7 | "strictMath": false, 8 | "strictUnits": false, 9 | "relativeUrls": true, 10 | "rootPath": "", 11 | "sourceMapRoot": "", 12 | "sourceMapBasePath": "", 13 | "sourceMap": false 14 | }, 15 | "sass": { 16 | "autoPrefix": "", 17 | "includePath": "", 18 | "indentType": "space", 19 | "indentWidth": 2, 20 | "outputStyle": "nested", 21 | "Precision": 5, 22 | "relativeUrls": true, 23 | "sourceMapRoot": "", 24 | "lineFeed": "", 25 | "sourceMap": false 26 | }, 27 | "stylus": { 28 | "sourceMap": false 29 | }, 30 | "babel": { 31 | "sourceMap": false 32 | }, 33 | "coffeescript": { 34 | "bare": false, 35 | "runtimeMode": "node", 36 | "sourceMap": false 37 | }, 38 | "handlebars": { 39 | "root": "", 40 | "noBOM": false, 41 | "name": "", 42 | "namespace": "", 43 | "knownHelpersOnly": false, 44 | "forcePartial": false, 45 | "knownHelpers": [], 46 | "commonjs": "", 47 | "amd": false, 48 | "sourceMap": false 49 | } 50 | }, 51 | "minifiers": { 52 | "css": { 53 | "enabled": true, 54 | "termSemicolons": true, 55 | "gzip": false 56 | }, 57 | "javascript": { 58 | "enabled": true, 59 | "termSemicolons": true, 60 | "gzip": false 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Member List View for Umbraco 10+ 2 | 3 | ![Member List View Logo](https://raw.githubusercontent.com/YourITGroup/umbMemberListView/master/assets/Membership_logo.png) 4 | 5 | Nuget Package: 6 | [![NuGet release](https://img.shields.io/nuget/v/MemberListView.svg)](https://www.nuget.org/packages/MemberListView/) 7 | [![NuGet release](https://img.shields.io/nuget/dt/MemberListView.svg)](https://www.nuget.org/packages/MemberListView/) 8 | 9 | Umbraco Package: 10 | [![Our Umbraco project page](https://img.shields.io/badge/our-umbraco-orange.svg)](https://our.umbraco.com/packages/backoffice-extensions/memberlistview/) 11 | 12 | Adds a MemberListView dashboard to the Members area in Umbraco 10+ to allow easier management of members including approval and unlocking capabilities. 13 | The MemberListView for Umbraco 8 and 9 provides a management dashboard view for Members with convenient filtering and sorting and allows for mass Unlock, Suspension or Activation of members. 14 | 15 | Installing the package enables a new Manage Dashboard on the Members section. 16 | 17 | ## Features 18 | 19 | The Member List View has been designed to be similar to the ContentListView property editor and features a Create button to allow for quick creation of new Members and context-sensitive action buttons to Unlock, Approve or Suspend members depending on the status of their account as well as the ability to Delete them. All actions can be performed on batches of users, as they apply to selected users only. 20 | 21 | ## Member Editing 22 | 23 | Member Editing can be done without leaving the MemberListView by clicking on a member name, which creates a Dialog pulled in from the right side to edit the member. 24 | 25 | ## Version History 26 | 27 | **Version 3.0.0** 28 | 29 | * Now supporting Umbraco 10 and 11 only 30 | * Exporting now supports exporting only selected members (Github issue #21) 31 | 32 | **Version 2.2.0** 33 | 34 | * Now Dual-Targeting dotNet Framework and dotnet5 to upport Umbraco 8 and 9 from the same package 35 | 36 | **Version 2.1.1** 37 | 38 | * Fixed issue filtering on Member Groups 39 | 40 | **Version 2.0.9** 41 | 42 | * Fixes to work with Umbraco 8.12 - previously wasn't enforcing `"strict mode";` which was causing everything to fall in a heap. 43 | * Aesthetic changes to checkboxes in dialogs 44 | 45 | **Version 2.0.7** 46 | 47 | * Various bug fixes including fix for Suspended/Locked filtering. 48 | 49 | **Version 2.0.4** 50 | 51 | * Backwards compatibility fix for Umbraco 8.6 52 | 53 | **Version 2.0.0** 54 | 55 | * Re-written for Umbraco 8 🎉 56 | 57 | **Version 1.5.5** 58 | 59 | * Shiny new icon - that's it 😎 60 | 61 | **Version 1.5.4** 62 | 63 | * Improved handling of bulk operations - refresh should now display correct items after a short delay to allow the index to catch up. 64 | 65 | **Version 1.5.3** 66 | 67 | * Added support for SensitiveData - Exports will be hidden if the user doesn't have permission to view sensitive data, and sensitive properties will not be viewable 68 | * Enhanced Export functionality - now allows selection of columns and the choice of exporting to CSV or Excel OpenFormat 69 | 70 | **Version 1.5.0** 71 | 72 | * **Compiled against Umbraco 7.15.1** 73 | * Refreshed to be more inline with current Umbraco styling and practices. 74 | * Filtering and columns now much easier to customise. 75 | * Added multiple Member Group filtering out of the box 76 | 77 | **Version 1.3.0** 78 | * Support for Localization with plugin-based lang files 79 | * Fixed issue where selecting a row caused the "You have unsaved changes" message to appear on navigation. 80 | 81 | **Version 1.2.0** 82 | 83 | * Compiled against Umbraco 7.7 84 | * Fixed issue with suspending/activating/deleting members 85 | 86 | **Version 0.9.12** 87 | 88 | * Fixed "Error: Argument 'MemberManager.Dashboard.MemberListViewController' is not a function" issue due to update in ClientDependency module 89 | 90 | **Version 0.9.11** 91 | 92 | * UI cleanup - paging now consistent with 7.2.0 ListView 93 | * Issue with UnApproved members showing up in the list as approved. 94 | 95 | **Version 0.9.10** 96 | 97 | * Export - Export now supports additional user indexed fields. If you add a property to a member type and want it to be exported, add it to the IndexUserFields collection in the ExamineIndex.config file. 98 | 99 | **Version 0.9.9** 100 | 101 | * Search is now lightning fast, although some modification to the Examine Index configuration is required. 102 | * Filtering - filtering has now been moved to a dialog, includes Membership Flags and MemberType as default fields and is extendable. 103 | * Export - Filtered members can now be exported to a CSV file containing basic fields. 104 | 105 | ## Sample Web project: 106 | 107 | * Uses SqlCe database - username is "**admin@admin.com**"; password is "**Password123**" 108 | * Umbraco 11 109 | 110 | The Sample project has three member properties: `First Name`, `Last Name` and `Phone Number`. `Phone Number` has also been marked as Sensitive. 111 | The version of MemberListView in the sample project has been modified to include these properties in the list. 112 | 113 | ## Logo 114 | The package logo uses the Family (by Oksana Latysheva, UA) icon from the Noun Project, licensed under CC BY 3.0 US. 115 | -------------------------------------------------------------------------------- /README.nuget.md: -------------------------------------------------------------------------------- 1 | # Member List View for Umbraco 10+ 2 | 3 | ![Member List View Logo](https://raw.githubusercontent.com/YourITGroup/umbMemberListView/master/assets/Membership_logo.png) 4 | 5 | Adds a MemberListView dashboard to the Members area in Umbraco 10+ to allow easier management of members including approval and unlocking capabilities. 6 | The MemberListView for Umbraco 10+ provides a management dashboard view for Members with convenient filtering and sorting and allows for mass Unlock, Suspension or Activation of members. 7 | 8 | Installing the package enables a new Manage Dashboard on the Members section. 9 | 10 | ## Features 11 | 12 | The Member List View has been designed to be similar to the ContentListView property editor and features a Create button to allow for quick creation of new Members and context-sensitive action buttons to Unlock, Approve or Suspend members depending on the status of their account as well as the ability to Delete them. All actions can be performed on batches of users, as they apply to selected users only. 13 | 14 | ## Member Editing 15 | 16 | Member Editing can be done without leaving the MemberListView by clicking on a member name, which creates a Dialog pulled in from the right side to edit the member. 17 | 18 | ## Logo 19 | The package logo uses the Family (by Oksana Latysheva, UA) icon from the Noun Project, licensed under CC BY 3.0 US. 20 | -------------------------------------------------------------------------------- /WebSample.V11/.gitignore: -------------------------------------------------------------------------------- 1 | ## 2 | ## Umbraco CMS 3 | ## 4 | 5 | # JSON schema files for appsettings.json 6 | appsettings-schema.json 7 | appsettings-schema.*.json 8 | 9 | # Packages created from the backoffice (package.xml/package.zip) 10 | /umbraco/Data/CreatedPackages/ 11 | 12 | # Temp folder containing Examine indexes, NuCache, MediaCache, etc. 13 | /umbraco/Data/TEMP/ 14 | 15 | # SQLite database files 16 | #/umbraco/Data/*.sqlite.db 17 | /umbraco/Data/*.sqlite.db-shm 18 | /umbraco/Data/*.sqlite.db-wal 19 | 20 | # Log files 21 | /umbraco/Logs/ 22 | 23 | # Media files 24 | /wwwroot/media/ 25 | 26 | /package 27 | /App_Plugins/MemberListView -------------------------------------------------------------------------------- /WebSample.V11/Program.cs: -------------------------------------------------------------------------------- 1 | namespace WebSample.V11 2 | { 3 | public class Program 4 | { 5 | public static void Main(string[] args) 6 | => CreateHostBuilder(args) 7 | .Build() 8 | .Run(); 9 | 10 | public static IHostBuilder CreateHostBuilder(string[] args) => 11 | Host.CreateDefaultBuilder(args) 12 | .ConfigureUmbracoDefaults() 13 | .ConfigureWebHostDefaults(webBuilder => 14 | { 15 | webBuilder.UseStaticWebAssets(); 16 | webBuilder.UseStartup(); 17 | }); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /WebSample.V11/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:40429", 8 | "sslPort": 44395 9 | } 10 | }, 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "environmentVariables": { 16 | "ASPNETCORE_ENVIRONMENT": "Development" 17 | } 18 | }, 19 | "Umbraco.Web.UI": { 20 | "commandName": "Project", 21 | "dotnetRunMessages": true, 22 | "launchBrowser": true, 23 | "applicationUrl": "https://localhost:44395;http://localhost:40429", 24 | "environmentVariables": { 25 | "ASPNETCORE_ENVIRONMENT": "Development" 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /WebSample.V11/Startup.cs: -------------------------------------------------------------------------------- 1 | namespace WebSample.V11 2 | { 3 | public class Startup 4 | { 5 | private readonly IWebHostEnvironment _env; 6 | private readonly IConfiguration _config; 7 | 8 | /// 9 | /// Initializes a new instance of the class. 10 | /// 11 | /// The web hosting environment. 12 | /// The configuration. 13 | /// 14 | /// Only a few services are possible to be injected here https://github.com/dotnet/aspnetcore/issues/9337. 15 | /// 16 | public Startup(IWebHostEnvironment webHostEnvironment, IConfiguration config) 17 | { 18 | _env = webHostEnvironment ?? throw new ArgumentNullException(nameof(webHostEnvironment)); 19 | _config = config ?? throw new ArgumentNullException(nameof(config)); 20 | } 21 | 22 | /// 23 | /// Configures the services. 24 | /// 25 | /// The services. 26 | /// 27 | /// This method gets called by the runtime. Use this method to add services to the container. 28 | /// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940. 29 | /// 30 | public void ConfigureServices(IServiceCollection services) 31 | { 32 | services.AddUmbraco(_env, _config) 33 | .AddBackOffice() 34 | .AddWebsite() 35 | .AddComposers() 36 | .Build(); 37 | } 38 | 39 | /// 40 | /// Configures the application. 41 | /// 42 | /// The application builder. 43 | /// The web hosting environment. 44 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 45 | { 46 | if (env.IsDevelopment()) 47 | { 48 | app.UseDeveloperExceptionPage(); 49 | } 50 | 51 | app.UseHttpsRedirection(); 52 | 53 | app.UseUmbraco() 54 | .WithMiddleware(u => 55 | { 56 | u.UseBackOffice(); 57 | u.UseWebsite(); 58 | }) 59 | .WithEndpoints(u => 60 | { 61 | u.UseInstallerEndpoints(); 62 | u.UseBackOfficeEndpoints(); 63 | u.UseWebsiteEndpoints(); 64 | }); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /WebSample.V11/Views/Partials/blockgrid/area.cshtml: -------------------------------------------------------------------------------- 1 | @using Umbraco.Extensions 2 | @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage 3 | 4 |
9 | @await Html.GetBlockGridItemsHtmlAsync(Model) 10 |
11 | -------------------------------------------------------------------------------- /WebSample.V11/Views/Partials/blockgrid/areas.cshtml: -------------------------------------------------------------------------------- 1 | @using Umbraco.Extensions 2 | @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage 3 | @{ 4 | if (Model?.Areas.Any() != true) { return; } 5 | } 6 | 7 |
9 | @foreach (var area in Model.Areas) 10 | { 11 | @await Html.GetBlockGridItemAreaHtmlAsync(area) 12 | } 13 |
14 | -------------------------------------------------------------------------------- /WebSample.V11/Views/Partials/blockgrid/default.cshtml: -------------------------------------------------------------------------------- 1 | @using Umbraco.Extensions 2 | @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage 3 | @{ 4 | if (Model?.Any() != true) { return; } 5 | } 6 | 7 |
10 | @await Html.GetBlockGridItemsHtmlAsync(Model) 11 |
12 | -------------------------------------------------------------------------------- /WebSample.V11/Views/Partials/blockgrid/items.cshtml: -------------------------------------------------------------------------------- 1 | @using Umbraco.Cms.Core.Models.Blocks 2 | @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage> 3 | @{ 4 | if (Model?.Any() != true) { return; } 5 | } 6 | 7 |
8 | @foreach (var item in Model) 9 | { 10 | 11 |
19 | @{ 20 | var partialViewName = "blockgrid/Components/" + item.Content.ContentType.Alias; 21 | try 22 | { 23 | @await Html.PartialAsync(partialViewName, item) 24 | } 25 | catch (InvalidOperationException) 26 | { 27 |

28 | Could not render component of type: @(item.Content.ContentType.Alias) 29 |
30 | This likely happened because the partial view @partialViewName could not be found. 31 |

32 | } 33 | } 34 |
35 | } 36 |
37 | -------------------------------------------------------------------------------- /WebSample.V11/Views/Partials/blocklist/default.cshtml: -------------------------------------------------------------------------------- 1 | @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage 2 | @{ 3 | if (Model?.Any() != true) { return; } 4 | } 5 |
6 | @foreach (var block in Model) 7 | { 8 | if (block?.ContentUdi == null) { continue; } 9 | var data = block.Content; 10 | 11 | @await Html.PartialAsync("blocklist/Components/" + data.ContentType.Alias, block) 12 | } 13 |
14 | -------------------------------------------------------------------------------- /WebSample.V11/Views/Partials/grid/bootstrap3-fluid.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Web 2 | @using Microsoft.AspNetCore.Html 3 | @using Newtonsoft.Json.Linq 4 | @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage 5 | 6 | @* 7 | Razor helpers located at the bottom of this file 8 | *@ 9 | 10 | @if (Model is JObject && Model?.sections is not null) 11 | { 12 | var oneColumn = ((System.Collections.ICollection)Model.sections).Count == 1; 13 | 14 |
15 | @if (oneColumn) 16 | { 17 | foreach (var section in Model.sections) 18 | { 19 |
20 | @foreach (var row in section.rows) 21 | { 22 | renderRow(row); 23 | } 24 |
25 | } 26 | } 27 | else 28 | { 29 |
30 | @foreach (var sec in Model.sections) 31 | { 32 |
33 |
34 | @foreach (var row in sec.rows) 35 | { 36 | renderRow(row); 37 | } 38 |
39 |
40 | } 41 |
42 | } 43 |
44 | } 45 | 46 | @functions{ 47 | 48 | private async Task renderRow(dynamic row) 49 | { 50 |
51 |
52 | @foreach (var area in row.areas) 53 | { 54 |
55 |
56 | @foreach (var control in area.controls) 57 | { 58 | if (control?.editor?.view != null) 59 | { 60 | @await Html.PartialAsync("grid/editors/base", (object)control) 61 | } 62 | } 63 |
64 |
65 | } 66 |
67 |
68 | } 69 | } 70 | 71 | @functions{ 72 | 73 | public static HtmlString RenderElementAttributes(dynamic contentItem) 74 | { 75 | var attrs = new List(); 76 | JObject cfg = contentItem.config; 77 | 78 | if (cfg != null) 79 | { 80 | foreach (JProperty property in cfg.Properties()) 81 | { 82 | var propertyValue = HttpUtility.HtmlAttributeEncode(property.Value.ToString()); 83 | attrs.Add(property.Name + "=\"" + propertyValue + "\""); 84 | } 85 | } 86 | 87 | JObject style = contentItem.styles; 88 | 89 | if (style != null) { 90 | var cssVals = new List(); 91 | foreach (JProperty property in style.Properties()) 92 | { 93 | var propertyValue = property.Value.ToString(); 94 | if (string.IsNullOrWhiteSpace(propertyValue) == false) 95 | { 96 | cssVals.Add(property.Name + ":" + propertyValue + ";"); 97 | } 98 | } 99 | 100 | if (cssVals.Any()) 101 | attrs.Add("style='" + HttpUtility.HtmlAttributeEncode(string.Join(" ", cssVals)) + "'"); 102 | } 103 | 104 | return new HtmlString(string.Join(" ", attrs)); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /WebSample.V11/Views/Partials/grid/bootstrap3.cshtml: -------------------------------------------------------------------------------- 1 | @using System.Web 2 | @using Microsoft.AspNetCore.Html 3 | @using Newtonsoft.Json.Linq 4 | @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage 5 | 6 | @if (Model is JObject && Model?.sections is not null) 7 | { 8 | var oneColumn = ((System.Collections.ICollection)Model.sections).Count == 1; 9 | 10 |
11 | @if (oneColumn) 12 | { 13 | foreach (var section in Model.sections) 14 | { 15 |
16 | @foreach (var row in section.rows) 17 | { 18 | renderRow(row, true); 19 | } 20 |
21 | } 22 | } 23 | else 24 | { 25 |
26 |
27 | @foreach (var sec in Model.sections) 28 | { 29 |
30 |
31 | @foreach (var row in sec.rows) 32 | { 33 | renderRow(row, false); 34 | } 35 |
36 |
37 | } 38 |
39 |
40 | } 41 |
42 | } 43 | 44 | @functions{ 45 | 46 | private async Task renderRow(dynamic row, bool singleColumn) 47 | { 48 |
49 | @if (singleColumn) { 50 | @:
51 | } 52 |
53 | @foreach (var area in row.areas) 54 | { 55 |
56 |
57 | @foreach (var control in area.controls) 58 | { 59 | if (control?.editor?.view != null) 60 | { 61 | @await Html.PartialAsync("grid/editors/base", (object)control) 62 | } 63 | } 64 |
65 |
66 | } 67 |
68 | @if (singleColumn) { 69 | @:
70 | } 71 |
72 | } 73 | 74 | } 75 | 76 | @functions{ 77 | 78 | public static HtmlString RenderElementAttributes(dynamic contentItem) 79 | { 80 | var attrs = new List(); 81 | JObject cfg = contentItem.config; 82 | 83 | if (cfg != null) 84 | { 85 | foreach (JProperty property in cfg.Properties()) 86 | { 87 | var propertyValue = HttpUtility.HtmlAttributeEncode(property.Value.ToString()); 88 | attrs.Add(property.Name + "=\"" + propertyValue + "\""); 89 | } 90 | } 91 | 92 | JObject style = contentItem.styles; 93 | 94 | if (style != null) 95 | { 96 | var cssVals = new List(); 97 | foreach (JProperty property in style.Properties()) 98 | { 99 | var propertyValue = property.Value.ToString(); 100 | if (string.IsNullOrWhiteSpace(propertyValue) == false) 101 | { 102 | cssVals.Add(property.Name + ":" + propertyValue + ";"); 103 | } 104 | } 105 | 106 | if (cssVals.Any()) 107 | attrs.Add("style=\"" + HttpUtility.HtmlAttributeEncode(string.Join(" ", cssVals)) + "\""); 108 | } 109 | 110 | return new HtmlString(string.Join(" ", attrs)); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /WebSample.V11/Views/Partials/grid/editors/base.cshtml: -------------------------------------------------------------------------------- 1 | @model dynamic 2 | 3 | @try 4 | { 5 | string editor = EditorView(Model); 6 | @await Html.PartialAsync(editor, Model as object) 7 | } 8 | catch (Exception ex) 9 | { 10 |
@ex.ToString()
11 | } 12 | 13 | @functions{ 14 | 15 | public static string EditorView(dynamic contentItem) 16 | { 17 | string view = contentItem.editor.render != null ? contentItem.editor.render.ToString() : contentItem.editor.view.ToString(); 18 | view = view.Replace(".html", ".cshtml"); 19 | 20 | if (!view.Contains("/")) 21 | { 22 | view = "grid/editors/" + view; 23 | } 24 | 25 | return view; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /WebSample.V11/Views/Partials/grid/editors/embed.cshtml: -------------------------------------------------------------------------------- 1 | @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage 2 | 3 | @if (Model is not null) 4 | { 5 | string embedValue = Convert.ToString(Model.value); 6 | embedValue = embedValue.DetectIsJson() ? Model.value.preview : Model.value; 7 | 8 |
9 | @Html.Raw(embedValue) 10 |
11 | } 12 | -------------------------------------------------------------------------------- /WebSample.V11/Views/Partials/grid/editors/macro.cshtml: -------------------------------------------------------------------------------- 1 | @inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage 2 | 3 | @if (Model?.value is not null) 4 | { 5 | string macroAlias = Model.value.macroAlias.ToString(); 6 | var parameters = new Dictionary(); 7 | foreach (var mpd in Model.value.macroParamsDictionary) 8 | { 9 | parameters.Add(mpd.Name, mpd.Value); 10 | } 11 | 12 | 13 | @await Umbraco.RenderMacroAsync(macroAlias, parameters) 14 | 15 | } 16 | -------------------------------------------------------------------------------- /WebSample.V11/Views/Partials/grid/editors/media.cshtml: -------------------------------------------------------------------------------- 1 | @model dynamic 2 | @using Umbraco.Cms.Core.Media 3 | @using Umbraco.Cms.Core.PropertyEditors.ValueConverters 4 | @inject IImageUrlGenerator ImageUrlGenerator 5 | 6 | @if (Model?.value is not null) 7 | { 8 | var url = Model.value.image; 9 | 10 | if (Model.editor.config != null && Model.editor.config.size != null) 11 | { 12 | if (Model.value.coordinates != null) 13 | { 14 | url = ImageCropperTemplateCoreExtensions.GetCropUrl( 15 | (string)url, 16 | ImageUrlGenerator, 17 | width: (int)Model.editor.config.size.width, 18 | height: (int)Model.editor.config.size.height, 19 | cropAlias: "default", 20 | cropDataSet: new ImageCropperValue 21 | { 22 | Crops = new[] 23 | { 24 | new ImageCropperValue.ImageCropperCrop 25 | { 26 | Alias = "default", 27 | Coordinates = new ImageCropperValue.ImageCropperCropCoordinates 28 | { 29 | X1 = (decimal)Model.value.coordinates.x1, 30 | Y1 = (decimal)Model.value.coordinates.y1, 31 | X2 = (decimal)Model.value.coordinates.x2, 32 | Y2 = (decimal)Model.value.coordinates.y2 33 | } 34 | } 35 | } 36 | }); 37 | } 38 | else 39 | { 40 | url = ImageCropperTemplateCoreExtensions.GetCropUrl( 41 | (string)url, 42 | ImageUrlGenerator, 43 | width: (int)Model.editor.config.size.width, 44 | height: (int)Model.editor.config.size.height, 45 | cropDataSet: new ImageCropperValue 46 | { 47 | FocalPoint = new ImageCropperValue.ImageCropperFocalPoint 48 | { 49 | Top = Model.value.focalPoint == null ? 0.5m : Model.value.focalPoint.top, 50 | Left = Model.value.focalPoint == null ? 0.5m : Model.value.focalPoint.left 51 | } 52 | }); 53 | } 54 | } 55 | 56 | var altText = Model.value.altText ?? Model.value.caption ?? string.Empty; 57 | 58 | @altText 59 | 60 | if (Model.value.caption != null) 61 | { 62 |

@Model.value.caption

63 | } 64 | } 65 | -------------------------------------------------------------------------------- /WebSample.V11/Views/Partials/grid/editors/rte.cshtml: -------------------------------------------------------------------------------- 1 | @using Umbraco.Cms.Core.Templates 2 | @model dynamic 3 | @inject HtmlLocalLinkParser HtmlLocalLinkParser; 4 | @inject HtmlUrlParser HtmlUrlParser; 5 | @inject HtmlImageSourceParser HtmlImageSourceParser; 6 | 7 | @{ 8 | var value = HtmlLocalLinkParser.EnsureInternalLinks(Model?.value.ToString()); 9 | value = HtmlUrlParser.EnsureUrls(value); 10 | value = HtmlImageSourceParser.EnsureImageSources(value); 11 | } 12 | 13 | @Html.Raw(value) 14 | -------------------------------------------------------------------------------- /WebSample.V11/Views/Partials/grid/editors/textstring.cshtml: -------------------------------------------------------------------------------- 1 | @model dynamic 2 | 3 | @if (Model?.editor.config.markup is not null) 4 | { 5 | string markup = Model.editor.config.markup.ToString(); 6 | markup = markup.Replace("#value#", Html.ReplaceLineBreaks((string)Model.value.ToString()).ToString()); 7 | 8 | if (Model.editor.config.style != null) 9 | { 10 | markup = markup.Replace("#style#", Model.editor.config.style.ToString()); 11 | } 12 | 13 | 14 | @Html.Raw(markup) 15 | 16 | } 17 | else 18 | { 19 | 20 |
@Model?.value
21 |
22 | } 23 | -------------------------------------------------------------------------------- /WebSample.V11/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using Umbraco.Extensions 2 | @using WebSample.V11 3 | @using Umbraco.Cms.Web.Common.PublishedModels 4 | @using Umbraco.Cms.Web.Common.Views 5 | @using Umbraco.Cms.Core.Models.PublishedContent 6 | @using Microsoft.AspNetCore.Html 7 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 8 | @addTagHelper *, Smidge 9 | @inject Smidge.SmidgeHelper SmidgeHelper 10 | -------------------------------------------------------------------------------- /WebSample.V11/WebSample.V11.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net7.0 4 | enable 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | true 21 | 22 | 23 | 24 | 25 | false 26 | false 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /WebSample.V11/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "appsettings-schema.json", 3 | "Serilog": { 4 | "MinimumLevel": { 5 | "Default": "Information" 6 | }, 7 | "WriteTo": [ 8 | { 9 | "Name": "Async", 10 | "Args": { 11 | "configure": [ 12 | { 13 | "Name": "Console" 14 | } 15 | ] 16 | } 17 | } 18 | ] 19 | }, 20 | "ConnectionStrings": { 21 | "umbracoDbDSN": "Data Source=|DataDirectory|/Umbraco.sqlite.db;Cache=Shared;Foreign Keys=True;Pooling=True", 22 | "umbracoDbDSN_ProviderName": "Microsoft.Data.Sqlite" 23 | }, 24 | "Umbraco": { 25 | "CMS": { 26 | "Unattended": { 27 | "InstallUnattended": true, 28 | "UnattendedUserName": "admin@admin.com", 29 | "UnattendedUserEmail": "admin@admin.com", 30 | "UnattendedUserPassword": "Password123" 31 | }, 32 | "Content": { 33 | "MacroErrors": "Throw" 34 | }, 35 | "Hosting": { 36 | "Debug": true 37 | }, 38 | "RuntimeMinification": { 39 | "UseInMemoryCache": true, 40 | "CacheBuster": "Timestamp" 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /WebSample.V11/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "appsettings-schema.json", 3 | "Serilog": { 4 | "MinimumLevel": { 5 | "Default": "Information", 6 | "Override": { 7 | "Microsoft": "Warning", 8 | "Microsoft.Hosting.Lifetime": "Information", 9 | "System": "Warning" 10 | } 11 | } 12 | }, 13 | "Umbraco": { 14 | "CMS": { 15 | "Global": { 16 | "Id": "d7e65d5e-f0a8-441d-a819-6f06b3f59a6c", 17 | "UseHttps": true, 18 | "SanitizeTinyMce": true 19 | }, 20 | "Content": { 21 | "AllowEditInvariantFromNonDefault": true, 22 | "ContentVersionCleanupPolicy": { 23 | "EnableCleanup": true 24 | } 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /WebSample.V11/umbraco/Data/Umbraco.sqlite.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YourITGroup/MemberListView/87adedb2da8f74841b637a41a91e6fd93a254321/WebSample.V11/umbraco/Data/Umbraco.sqlite.db -------------------------------------------------------------------------------- /WebSample.V11/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YourITGroup/MemberListView/87adedb2da8f74841b637a41a91e6fd93a254321/WebSample.V11/wwwroot/favicon.ico -------------------------------------------------------------------------------- /WebSampleV12/Program.cs: -------------------------------------------------------------------------------- 1 | namespace WebSampleV12 2 | { 3 | public class Program 4 | { 5 | public static void Main(string[] args) 6 | => CreateHostBuilder(args) 7 | .Build() 8 | .Run(); 9 | 10 | public static IHostBuilder CreateHostBuilder(string[] args) => 11 | Host.CreateDefaultBuilder(args) 12 | .ConfigureUmbracoDefaults() 13 | .ConfigureWebHostDefaults(webBuilder => 14 | { 15 | webBuilder.UseStaticWebAssets(); 16 | webBuilder.UseStartup(); 17 | }); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /WebSampleV12/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/launchsettings.json", 3 | "iisSettings": { 4 | "windowsAuthentication": false, 5 | "anonymousAuthentication": true, 6 | "iisExpress": { 7 | "applicationUrl": "http://localhost:6786", 8 | "sslPort": 44348 9 | } 10 | }, 11 | "profiles": { 12 | "IIS Express": { 13 | "commandName": "IISExpress", 14 | "launchBrowser": true, 15 | "environmentVariables": { 16 | "ASPNETCORE_ENVIRONMENT": "Development" 17 | } 18 | }, 19 | "Umbraco.Web.UI": { 20 | "commandName": "Project", 21 | "dotnetRunMessages": true, 22 | "launchBrowser": true, 23 | "applicationUrl": "https://localhost:44348;http://localhost:6786", 24 | "environmentVariables": { 25 | "ASPNETCORE_ENVIRONMENT": "Development" 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /WebSampleV12/Startup.cs: -------------------------------------------------------------------------------- 1 | namespace WebSampleV12 2 | { 3 | public class Startup 4 | { 5 | private readonly IWebHostEnvironment _env; 6 | private readonly IConfiguration _config; 7 | 8 | /// 9 | /// Initializes a new instance of the class. 10 | /// 11 | /// The web hosting environment. 12 | /// The configuration. 13 | /// 14 | /// Only a few services are possible to be injected here https://github.com/dotnet/aspnetcore/issues/9337. 15 | /// 16 | public Startup(IWebHostEnvironment webHostEnvironment, IConfiguration config) 17 | { 18 | _env = webHostEnvironment ?? throw new ArgumentNullException(nameof(webHostEnvironment)); 19 | _config = config ?? throw new ArgumentNullException(nameof(config)); 20 | } 21 | 22 | /// 23 | /// Configures the services. 24 | /// 25 | /// The services. 26 | /// 27 | /// This method gets called by the runtime. Use this method to add services to the container. 28 | /// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940. 29 | /// 30 | public void ConfigureServices(IServiceCollection services) 31 | { 32 | services.AddUmbraco(_env, _config) 33 | .AddBackOffice() 34 | .AddWebsite() 35 | .AddDeliveryApi() 36 | .AddComposers() 37 | .Build(); 38 | } 39 | 40 | /// 41 | /// Configures the application. 42 | /// 43 | /// The application builder. 44 | /// The web hosting environment. 45 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 46 | { 47 | if (env.IsDevelopment()) 48 | { 49 | app.UseDeveloperExceptionPage(); 50 | } 51 | 52 | app.UseHttpsRedirection(); 53 | 54 | app.UseUmbraco() 55 | .WithMiddleware(u => 56 | { 57 | u.UseBackOffice(); 58 | u.UseWebsite(); 59 | }) 60 | .WithEndpoints(u => 61 | { 62 | u.UseInstallerEndpoints(); 63 | u.UseBackOfficeEndpoints(); 64 | u.UseWebsiteEndpoints(); 65 | }); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /WebSampleV12/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using Umbraco.Extensions 2 | @using WebSampleV12 3 | @using Umbraco.Cms.Web.Common.PublishedModels 4 | @using Umbraco.Cms.Web.Common.Views 5 | @using Umbraco.Cms.Core.Models.PublishedContent 6 | @using Microsoft.AspNetCore.Html 7 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers 8 | @addTagHelper *, Smidge 9 | @inject Smidge.SmidgeHelper SmidgeHelper 10 | -------------------------------------------------------------------------------- /WebSampleV12/WebSampleV12.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net7.0 4 | enable 5 | enable 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | true 22 | 23 | 24 | 25 | 26 | 27 | 28 | false 29 | false 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /WebSampleV12/appsettings-schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "allOf": [ 4 | { 5 | "$ref": "https://json.schemastore.org/appsettings.json" 6 | }, 7 | { 8 | "$ref": "appsettings-schema.Umbraco.Cms.json#" 9 | } 10 | ] 11 | } -------------------------------------------------------------------------------- /WebSampleV12/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "appsettings-schema.json", 3 | "Serilog": { 4 | "MinimumLevel": { 5 | "Default": "Information" 6 | }, 7 | "WriteTo": [ 8 | { 9 | "Name": "Async", 10 | "Args": { 11 | "configure": [ 12 | { 13 | "Name": "Console" 14 | } 15 | ] 16 | } 17 | } 18 | ] 19 | }, 20 | "ConnectionStrings": { 21 | "umbracoDbDSN": "Data Source=|DataDirectory|/Umbraco.sqlite.db;Cache=Shared;Foreign Keys=True;Pooling=True", 22 | "umbracoDbDSN_ProviderName": "Microsoft.Data.Sqlite" 23 | }, 24 | "Umbraco": { 25 | "CMS": { 26 | "Unattended": { 27 | "InstallUnattended": true, 28 | "UnattendedUserName": "admin@admin.com", 29 | "UnattendedUserEmail": "admin@admin.com", 30 | "UnattendedUserPassword": "Password123" 31 | }, 32 | "Content": { 33 | "MacroErrors": "Throw" 34 | }, 35 | "Hosting": { 36 | "Debug": true 37 | }, 38 | "RuntimeMinification": { 39 | "UseInMemoryCache": true, 40 | "CacheBuster": "Timestamp" 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /WebSampleV12/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "appsettings-schema.json", 3 | "Serilog": { 4 | "MinimumLevel": { 5 | "Default": "Information", 6 | "Override": { 7 | "Microsoft": "Warning", 8 | "Microsoft.Hosting.Lifetime": "Information", 9 | "System": "Warning" 10 | } 11 | } 12 | }, 13 | "Umbraco": { 14 | "CMS": { 15 | "Global": { 16 | "Id": "29222435-9d73-4a5a-a436-74025d9b8b64", 17 | "UseHttps": true, 18 | "SanitizeTinyMce": true 19 | }, 20 | "Content": { 21 | "AllowEditInvariantFromNonDefault": true, 22 | "ContentVersionCleanupPolicy": { 23 | "EnableCleanup": true 24 | } 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /WebSampleV12/umbraco/Data/Umbraco.sqlite.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YourITGroup/MemberListView/87adedb2da8f74841b637a41a91e6fd93a254321/WebSampleV12/umbraco/Data/Umbraco.sqlite.db -------------------------------------------------------------------------------- /WebSampleV12/wwwroot/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YourITGroup/MemberListView/87adedb2da8f74841b637a41a91e6fd93a254321/WebSampleV12/wwwroot/favicon.ico -------------------------------------------------------------------------------- /assets/Membership_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YourITGroup/MemberListView/87adedb2da8f74841b637a41a91e6fd93a254321/assets/Membership_logo.png -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | $Name$ 6 | $Version$ 7 | https://raw.githubusercontent.com/YourITGroup/umbMemberListView/master/assets/Membership_logo.png 8 | MIT 9 | https://github.com/YourITGroup/umbMemberListView 10 | 11 | 8 12 | 6 13 | 0 14 | 15 | 16 | 17 | Robert Foster 18 | http://youritteam.com.au 19 | 20 | 21 | robertjf72 22 | 23 | 24 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /umbraco-marketplace.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/umbraco/Umbraco.Deploy.Contrib/master/schema.json", 3 | "AuthorDetails": { 4 | "Name": "Robert Foster", 5 | "Description": "Robert is the founder of Your IT Team, an Umbraco Gold Partner; and is a seasoned Umbraco Master and MVP.", 6 | "Url": "http://youritteam.com.au", 7 | "ImageUrl": "https://our.umbraco.com/media/upload/87499f6e-4497-4cb0-a2b6-bf03e222cfc0/220427_ROB_FIONA_0031.jpeg?width=250&height=250&mode=crop" 8 | }, 9 | "Title": "Member ListView", 10 | "Category": "PIM & DAM", 11 | "LicenseTypes": [ "Apache-2.0" ], 12 | "PackageType": "Package", 13 | "PackagesByAuthor": [ "OpenOrClosed" ], 14 | "RelatedPackages": [], 15 | "Screenshots": [], 16 | "Tags": [ "Headless", "Membership" ], 17 | "VideoUrl": "" 18 | } 19 | --------------------------------------------------------------------------------