diff options
| -rwxr-xr-x | edit.php | 80 | ||||
| -rwxr-xr-x | includes/classes/StockAssembly.php | 25 | ||||
| -rwxr-xr-x | templates/edit_assembly.tpl | 73 | ||||
| -rwxr-xr-x | templates/view_assembly.tpl | 8 |
4 files changed, 184 insertions, 2 deletions
@@ -12,7 +12,7 @@ namespace Bitweaver\Stock; require_once '../kernel/includes/setup_inc.php'; use Bitweaver\KernelTools; -global $gBitSystem; +global $gBitSystem, $gBitDb; include_once LIBERTY_PKG_INCLUDE_PATH.'liberty_lib.php'; include_once STOCK_PKG_INCLUDE_PATH.'assembly_lookup_inc.php'; @@ -61,11 +61,89 @@ if( !empty( $_REQUEST['savegallery'] ) ) { } elseif( !empty($_REQUEST['cancelgallery'] ) ) { header( 'Location: '.$gContent->getDisplayUrl() ); die(); + +} elseif( $gContent->isValid() && !empty( $_REQUEST['add_component'] ) ) { + $title = trim( $_REQUEST['component_title'] ?? '' ); + if( $title !== '' ) { + $row = $gBitDb->getRow( + "SELECT lc.`content_id` FROM `".BIT_DB_PREFIX."liberty_content` lc + WHERE UPPER(lc.`title`) = UPPER(?) AND lc.`content_type_guid` = 'stockcomponent'", + [ $title ] + ); + if( $row ) { + $nextPos = ((int)$gBitDb->getOne( + "SELECT MAX(`item_position`) FROM `".BIT_DB_PREFIX."stock_assembly_component_map` WHERE `assembly_content_id`=?", + [ $gContent->mContentId ] + )) + 1; + if( !$gContent->addItem( $row['content_id'], $nextPos ) ) { + $stockErrors[] = KernelTools::tra('Component already in this assembly:').' '.htmlspecialchars($title); + } + } else { + $stockErrors[] = KernelTools::tra('Component not found:').' '.htmlspecialchars($title); + } + } + if( empty( $stockErrors ) ) { + header( 'Location: '.STOCK_PKG_URL.'edit.php?content_id='.$gContent->mContentId ); + die(); + } + +} elseif( $gContent->isValid() && !empty( $_REQUEST['upload_components_csv'] ) ) { + $csvLoaded = $csvSkipped = 0; + $csvErrors = []; + if( !empty( $_FILES['csv_file']['tmp_name'] ) && is_uploaded_file( $_FILES['csv_file']['tmp_name'] ) ) { + $nextPos = ((int)$gBitDb->getOne( + "SELECT MAX(`item_position`) FROM `".BIT_DB_PREFIX."stock_assembly_component_map` WHERE `assembly_content_id`=?", + [ $gContent->mContentId ] + )) + 1; + if( ($fh = fopen( $_FILES['csv_file']['tmp_name'], 'r' )) !== false ) { + while( ($cols = fgetcsv($fh)) !== false ) { + $title = trim($cols[0]); + if( $title === '' || strtolower($title) === 'title' ) continue; + $row = $gBitDb->getRow( + "SELECT lc.`content_id` FROM `".BIT_DB_PREFIX."liberty_content` lc + WHERE UPPER(lc.`title`) = UPPER(?) AND lc.`content_type_guid` = 'stockcomponent'", + [ $title ] + ); + if( $row ) { + if( $gContent->addItem( $row['content_id'], $nextPos ) ) { + $nextPos++; + $csvLoaded++; + } else { + $csvSkipped++; + } + } else { + $csvErrors[] = KernelTools::tra('Not found:').' '.htmlspecialchars($title); + } + } + fclose($fh); + } + } + $gBitSmarty->assign( 'csvLoaded', $csvLoaded ); + $gBitSmarty->assign( 'csvSkipped', $csvSkipped ); + $gBitSmarty->assign( 'csvErrors', $csvErrors ); + +} elseif( $gContent->isValid() ) { + foreach( $_REQUEST as $k => $v ) { + if( preg_match( '/^remove_component_(\d+)$/', $k, $m ) ) { + $gContent->removeItem( (int)$m[1] ); + header( 'Location: '.STOCK_PKG_URL.'edit.php?content_id='.$gContent->mContentId ); + die(); + } + } } // Initalize the errors list which contains any errors which occured during storage $errors = !empty($gContent->mErrors) ? $gContent->mErrors : []; $gBitSmarty->assign('errors', $errors); +if( !empty($stockErrors) ) { + $gBitSmarty->assign('stockWarnings', $stockErrors); +} + +if( $gContent->isValid() ) { + $sortMode = $_REQUEST['sort_mode'] ?? 'item_position_asc'; + $gBitSmarty->assign( 'componentMap', $gContent->getComponentMapList($sortMode) ); + $gBitSmarty->assign( 'sortMode', $sortMode ); +} $gContent->mInfo['stockassembly_types'] = $gContent->getXrefGroupList(); diff --git a/includes/classes/StockAssembly.php b/includes/classes/StockAssembly.php index f0cd5f3..3d11e64 100755 --- a/includes/classes/StockAssembly.php +++ b/includes/classes/StockAssembly.php @@ -495,6 +495,31 @@ class StockAssembly extends StockBase { return count($this->mErrors) == 0; } + public function getComponentMapList( string $pSortMode = 'item_position_asc' ): array { + $ret = []; + if( $this->verifyId( $this->mContentId ) ) { + $orderby = match( $pSortMode ) { + 'title_asc' => 'lc.`title` ASC', + 'title_desc' => 'lc.`title` DESC', + 'item_position_desc' => 'fgim.`item_position` DESC, fgim.`item_content_id` DESC', + default => 'fgim.`item_position` ASC, fgim.`item_content_id` ASC', + }; + if( $rows = $this->mDb->query( + "SELECT fgim.`item_content_id`, fgim.`item_position`, lc.`title` + FROM `".BIT_DB_PREFIX."stock_assembly_component_map` fgim + INNER JOIN `".BIT_DB_PREFIX."liberty_content` lc ON (lc.`content_id` = fgim.`item_content_id`) + WHERE fgim.`assembly_content_id` = ? + ORDER BY $orderby", + [ $this->mContentId ] + ) ) { + foreach( $rows as $row ) { + $ret[$row['item_content_id']] = $row; + } + } + } + return $ret; + } + public function removeItem( $pContentId ) { $ret = false; if( $this->isValid() && @$this->verifyId( $pContentId ) ) { diff --git a/templates/edit_assembly.tpl b/templates/edit_assembly.tpl index 8ace0d9..4adec06 100755 --- a/templates/edit_assembly.tpl +++ b/templates/edit_assembly.tpl @@ -50,6 +50,79 @@ </div> {/form} + {* ── Components section ── *} + {if $gContent->isValid()} + <hr/> + <h3>{tr}Components{/tr}</h3> + + {* CSV upload results *} + {if isset($csvLoaded)} + <div class="alert alert-info"> + {tr}Loaded{/tr}: <strong>{$csvLoaded}</strong> + {if $csvSkipped} {tr}Skipped (duplicate){/tr}: <strong>{$csvSkipped}</strong>{/if} + </div> + {if $csvErrors} + <ul class="text-warning"> + {foreach from=$csvErrors item=msg}<li>{$msg|escape}</li>{/foreach} + </ul> + {/if} + {/if} + + {* Components table *} + {if $componentMap} + {form ipackage="stock" ifile="edit.php"} + <input type="hidden" name="content_id" value="{$gContent->mContentId|escape}"/> + <table class="table table-condensed table-striped"> + <thead> + <tr> + <th>{smartlink ititle="Pos" isort="item_position" ifile="edit.php" ipackage="stock" idefault=1 content_id=$gContent->mContentId}</th> + <th>{smartlink ititle="Component" isort="title" ifile="edit.php" ipackage="stock" content_id=$gContent->mContentId}</th> + <th></th> + </tr> + </thead> + <tbody> + {foreach from=$componentMap key=contentId item=comp} + <tr> + <td>{$comp.item_position|escape}</td> + <td>{$comp.title|escape}</td> + <td> + <input type="submit" class="btn btn-xs btn-default" + name="remove_component_{$contentId}" value="{tr}Remove{/tr}"/> + </td> + </tr> + {/foreach} + </tbody> + </table> + {/form} + {else} + <p class="muted">{tr}No components yet.{/tr}</p> + {/if} + + {* ── Add single component ── *} + <h4>{tr}Add Component{/tr}</h4> + {form ipackage="stock" ifile="edit.php"} + <input type="hidden" name="content_id" value="{$gContent->mContentId|escape}"/> + <div class="form-inline"> + <div class="form-group"> + <input type="text" class="form-control" name="component_title" + placeholder="{tr}Component title{/tr}" size="40"/> + </div> + <input type="submit" class="btn btn-default" name="add_component" value="{tr}Add{/tr}"/> + </div> + {/form} + + {* ── Upload CSV ── *} + <h4>{tr}Upload Components CSV{/tr}</h4> + <p class="help-block">{tr}One component title per line (or comma-separated with title in first column).{/tr}</p> + {form enctype="multipart/form-data" ipackage="stock" ifile="edit.php"} + <input type="hidden" name="content_id" value="{$gContent->mContentId|escape}"/> + <div class="form-inline"> + <input type="file" name="csv_file" accept=".csv,text/csv"/> + <input type="submit" class="btn btn-default" name="upload_components_csv" value="{tr}Upload{/tr}"/> + </div> + {/form} + {/if} + </div><!-- end .body --> </div><!-- end .stock --> {/strip} diff --git a/templates/view_assembly.tpl b/templates/view_assembly.tpl index f5e850c..bba6f74 100755 --- a/templates/view_assembly.tpl +++ b/templates/view_assembly.tpl @@ -1,4 +1,9 @@ {assign var=galLayout value=$gContent->getLayout()} +{if $gBitUser->hasPermission('p_stock_update')} + <div class="floaticon"> + <a title="{tr}Edit{/tr}" href="{$smarty.const.STOCK_PKG_URL}edit.php?content_id={$gContent->mContentId}">{booticon iname="fa-pen-to-square" iexplain="Edit Assembly"}</a> + </div> +{/if} {include file="`$smarty.const.STOCK_PKG_PATH`assembly_views/`$galLayout`/stock_`$galLayout`_inc.tpl"} {if $gContent->mInfo.stockassembly_types} @@ -7,7 +12,8 @@ {include file="bitpackage:liberty/list_xref.tpl" source=$gContent->mInfo.stockassembly_types[xrefGroup].source source_title=$gContent->mInfo.stockassembly_types[xrefGroup].title - group=$gContent->mInfo.stockassembly_types[xrefGroup].sort_order} + group=$gContent->mInfo.stockassembly_types[xrefGroup].sort_order + allow_edit=false} {/section} {/jstabs} {/if} |
