分类 电脑 下的文章

为 iredmail 1.7.1 安装 z-push 2.7.5

环境:Ubunt 24.04,iredmail 1.7.1 ,安装时,勾选了 SOGo 组件,因为需要使用它的 CalDAV 和 CardDAV 服务

步骤:

  1. apt install php-imap php-curl libawl-php php-curl php-xml php-ldap php-soap php-mbstring php-intl # 安装 php 扩展
  2. mkdir /usr/local/lib/z-push/ /var/log/z-push /var/lib/z-push # 创建 z-push 的相关目录
  3. chown www-data:www-data /var/log/z-push /var/lib/z-push # 目录给 web 用户权限
  4. wget https://github.com/Z-Hub/Z-Push/archive/refs/tags/2.7.5.tar.gz # 下载 z-push 文件
  5. cp -a src/* /usr/local/lib/z-push/ # 解压后,将得到的 src 目录里的文件,复制
  6. chown -R www-data:www-data /usr/local/lib/z-push/ # 给 web 用户权限
  7. vi /etc/nginx/templates/sogo.tmpl # 修改 sogo.tmpl 的文件内容,注释或删除原 SOGo 的 Microsoft-Server-ActiveSync 内容,用新的 z-push 替换:

    location ^~ /Microsoft-Server-ActiveSync {
     proxy_pass http://127.0.0.1:20000/SOGo/Microsoft-Server-ActiveSync;
    
     proxy_connect_timeout 3540;
     proxy_send_timeout 3540;
     proxy_read_timeout 3540;
    
     proxy_busy_buffers_size   64k;
     proxy_buffers             8 64k;
     proxy_buffer_size         64k;
    }
    
    location ^~ /SOGo/Microsoft-Server-ActiveSync {
     proxy_pass http://127.0.0.1:20000/SOGo/Microsoft-Server-ActiveSync;
    
     proxy_connect_timeout 3540;
     proxy_send_timeout 3540;
     proxy_read_timeout 3540;
    
     proxy_busy_buffers_size   64k;
     proxy_buffers             8 64k;
     proxy_buffer_size         64k;
    }

    新的:

    location /Microsoft-Server-ActiveSync {
             include /etc/nginx/fastcgi_params;
             fastcgi_param SCRIPT_FILENAME /usr/local/lib/z-push/index.php;
             fastcgi_read_timeout 630;
             include /etc/nginx/templates/fastcgi_php.tmpl;
             client_max_body_size 128M;
     }
     location ~* ^/autodiscover/autodiscover.xml$ {
             include /etc/nginx/fastcgi_params;
             fastcgi_param SCRIPT_FILENAME /usr/local/lib/z-push/autodiscover/autodiscover.php;
             include /etc/nginx/templates/fastcgi_php.tmpl;
    }
  8. systemctl restart nginx # 重启 nginx 服务
  9. vi /etc/php/8.3/fpm/php.ini # 调整 php.ini 的内容,启用 parse_ini_file 函数
  10. systemctl restart php8.3-fpm.service # 重启 php-fpm 服务
  11. vi /usr/local/lib/z-push/config.php # 编辑配置文件,其中两个需要注意的, TIMEZONE 和 BACKEND_PROVIDER 。本例,是分别设置为 上海时区 Asia/Shanghai 和 BackendCombined 即,后端数据存储是 混搭模式
  12. vi /usr/local/lib/z-push/backend/combined/config.php # 编辑混搭模式里的配置文件内容,将其中罗列的类型精简。如本例,只使用 BackendIMAP , BackendCardDAV 和 BackendCalDAV 。同时,要将目录的处理类型指定,避免出现指定不存在的 z 类型

     public static function GetBackendCombinedConfig() {
         //use a function for it because php does not allow
         //assigning variables to the class members (expecting T_STRING)
         return array(
             //the order in which the backends are loaded.
             //login only succeeds if all backend return true on login
             //sending mail: the mail is sent with first backend that is able to send the mail
             'backends' => array(
                 'i' => array(
                     'name' => 'BackendIMAP',
                 ),
                 'd' => array(
                     'name' => 'BackendCardDAV',
                 ),
                 'c' => array(
                     'name' => 'BackendCalDAV',
                 ),
             ),
             'delimiter' => '/',
             //force one type of folder to one backend
             //it must match one of the above defined backends
             'folderbackend' => array(
                 SYNC_FOLDER_TYPE_INBOX => 'i',
                 SYNC_FOLDER_TYPE_DRAFTS => 'i',
                 SYNC_FOLDER_TYPE_WASTEBASKET => 'i',
                 SYNC_FOLDER_TYPE_SENTMAIL => 'i',
                 SYNC_FOLDER_TYPE_OUTBOX => 'i',
                 SYNC_FOLDER_TYPE_TASK => 'c',
                 SYNC_FOLDER_TYPE_APPOINTMENT => 'c',
                 SYNC_FOLDER_TYPE_CONTACT => 'd',
                 SYNC_FOLDER_TYPE_NOTE => 'c',
                 SYNC_FOLDER_TYPE_JOURNAL => 'c',
                 SYNC_FOLDER_TYPE_OTHER => 'i',
                 SYNC_FOLDER_TYPE_UNKNOWN => 'i',
             ),
             //creating a new folder in the root folder should create a folder in one backend
             'rootcreatefolderbackend' => 'i',
         );
     }
  13. vi /usr/local/lib/z-push/backend/imap/config.php # 配置 imap 的连接配置文件内容。本例是将 IMAP_PORT 设置为 993 端口;IMAP_OPTIONS 设置为 /ssl/novalidate-cert ,即使用 SSL 加密连接,不验证证书;IMAP_FOLDER_CONFIGURED 设置为 true
  14. vi /usr/local/lib/z-push/backend/caldav/config.php # 配置 caldav 的连接配置文件内容。CALDAV_SERVER 填写完整的主机名,如 mail.example.com ;CALDAV_PATH 填写 /SOGo/dav/%u/Calendar/ ; CALDAV_PERSONAL 填写 /SOGo/dav/%u/Calendar/personal/ ;CALDAV_SUPPORTS_SYNC 可设置为 true
  15. vi /usr/local/lib/z-push/backend/carddav/config.php # 配置 carddav 的连接配置文件内容。CARDDAV_PATH 设置为 /SOGo/dav/%u/Contacts/ ; CARDDAV_DEFAULT_PATH 设置为 /SOGo/dav/%u/Contacts/personal/ ;注释掉 CARDDAV_GAL_PATH 和 CARDDAV_GAL_MIN_LENGTH 两行
  16. 在浏览器里访问 https://mail.example.com/Microsoft-Server-ActiveSync ,应该会弹框,需入邮箱地址和密码,能成功登录
  17. 在 Nine Mail app 里,使用兼容 Exchange ActiveSync 的账户类型添加,能发邮件、创建日历事件 和 联系人,并且能在 SOGo Webmail 里看到新建的内容
  18. 如有报错,请查看 /var/log/z-push/z-push-error.log 日志内容

参考:

在 magento 的产品详情页里隐藏产品数量框 - class="field qty"

问:在 magento 2.4.7-p3 默认主题里,可以让产品详情页里的“数量”不显示,但不影响加到购物车里吗?

答:可以尝试在 vendor/magento/module-catalog/view/frontend/templates/product/view/addtocart.phtml 里注释掉“数量”的代码内容,如以下部分:

        <div class="field qty">
            <label class="label" for="qty"><span><?= $block->escapeHtml(__('Qty')) ?></span></label>
            <div class="control">
                <input type="number"
                       name="qty"
                       id="qty"
                       min="0"
                       value="<?= $block->getProductDefaultQty() * 1 ?>"
                       title="<?= $block->escapeHtmlAttr(__('Qty')) ?>"
                       class="input-text qty"
                       data-validate="<?= $block->escapeHtml(json_encode($block->getQuantityValidators())) ?>"
                       />
            </div>
        </div>

参考:

hestiacp 面板的 文件管理器 报错 - Unknown Error - PubkeyAuthentication

问题:在 debian 11 里安装 hestiacp 1.8.12 面板后,点击 文件管理器 图标,然后前端提示出错 Unknown Error ,无法显示文件列表

尝试:/var/log/hestia/nginx-error.log 的相应错误信息是:

[error] 608#0: *367 FastCGI sent in stderr: "PHP message: PHP Fatal error:  Uncaught League\Flysystem\Sftp\Connectio    nErrorException: Could not login with username: tester, host: 127.0.0.1 in /usr/local/hestia/web/fm/vendor/league/flysystem-sftp/src/Sftp    Adapter.php:244
Stack trace:
#0 /usr/local/hestia/web/fm/vendor/league/flysystem-sftp/src/SftpAdapter.php(207): League\Flysystem\Sftp\SftpAdapter->login()
#1 /usr/local/hestia/web/fm/vendor/league/flysystem/src/Adapter/AbstractFtpAdapter.php(650): League\Flysystem\Sftp\SftpAdapter->connect(    )
#2 /usr/local/hestia/web/fm/vendor/league/flysystem-sftp/src/SftpAdapter.php(360): League\Flysystem\Adapter\AbstractFtpAdapter->getConne    ction()
#3 /usr/local/hestia/web/fm/vendor/league/flysystem/src/Adapter/AbstractFtpAdapter.php(338): League\Flysystem\Sftp\SftpAdapter->listDire    ctoryContents()
#4 /usr/local/hestia/web/fm/vendor/league/flysystem/src/Filesystem.php(272): League\Flysystem\Adapter\AbstractFtpAdapter->listContents()
#5 /usr/local/hestia/web/fm/backend/Services/Storage/Filesystem.php(266): League\Flysys" while reading response header from upstream, cl    ient: 113.75.31.223, server: _, request: "POST /fm/?r=/getdir HTTP/2.0", upstream: "fastcgi://unix:/run/hestia-php.sock:", host: 

详细检查 /etc/ssh/sshd_config 的文件内容。其中,PubkeyAuthentication no 如果有这一行,需要注释或删除掉,因为 文件管理器 是需要使用 key 来验证登录的。

参考:https://hestiacp.com/docs/server-administration/file-manager.html

DCImanager 5 - Windows 2025 - UnattendEFI.xml

问题:setup.exe /Unattend:x:\temp\Unattend.xml 后,在约 60% 的进度条,弹框提示安装失败
过程:查看 Panther\setuperr.log 文件内容,有如:

2024-12-06 19:41:00, Error                            WpxXmlDocument::ReportError (onecore\base\ntsetup\wpx\core\xml.cpp:157) - 0xc00ce50d:
2024-12-06 19:41:00, Error                                Invalid XML (line 196, position 142): A semi colon character was expected.

2024-12-06 19:41:00, Error                            WpxXmlDocument::LoadXml (onecore\base\ntsetup\wpx\core\xml.cpp:266) - 0x8007000d:
2024-12-06 19:41:00, Error                                The given data cannot be parsed
2024-12-06 19:41:00, Error                            WpxGetAnswerFileType (onecore\base\ntsetup\wpx\file\getfiletype.cpp:54) - 0x8007000d:
2024-12-06 19:41:00, Error                                Couldn't parse answer file contents as XML

查看相应的 Unattend.xml 内容,是:

        <RunSynchronousCommand wcm:action="add">
          <Description>finish</Description>
          <Order>9</Order>
          <Path>cmd /c echo cmd /c %windir%\setup\scripts\wget.exe -O- "http://10.8.8.8/dcimgr?func=osinstall.finish&id=1wJj42
pYVdjzzB9" >> %windir%\setup\scripts\SetupComplete.cmd</Path>
          <WillReboot>OnRequest</WillReboot>
        </RunSynchronousCommand>

而 DCIM 的 UnattendEFI.xml 文件内容是:

<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend">
    <settings pass="windowsPE">
        <component name="Microsoft-Windows-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <UserData>
                <AcceptEula>true</AcceptEula>
            </UserData>
            <DiskConfiguration>
                <WillShowUI>OnError</WillShowUI>
                <Disk wcm:action="add">
                    <CreatePartitions>
                        <CreatePartition wcm:action="add">
                            <Order>1</Order>
                            <Size>400</Size>
                            <Type>EFI</Type>
                        </CreatePartition>
                        <CreatePartition wcm:action="add">
                            <Order>2</Order>
                            <Type>MSR</Type>
                            <Size>128</Size>
                        </CreatePartition>
                        <CreatePartition wcm:action="add">
                            <Order>3</Order>
                            <Extend>true</Extend>
                            <Type>Primary</Type>
                        </CreatePartition>
                    </CreatePartitions>
                    <ModifyPartitions>
                        <ModifyPartition wcm:action="add">
                            <Format>FAT32</Format>
                            <Label>System</Label>
                            <Order>1</Order>
                            <PartitionID>1</PartitionID>
                        </ModifyPartition>
                        <ModifyPartition wcm:action="add">
                            <Order>2</Order>
                            <PartitionID>3</PartitionID>
                            <Label>Windows</Label>
                            <Letter>C</Letter>
                            <Format>NTFS</Format>
                        </ModifyPartition>
                    </ModifyPartitions>
                    <DiskID>0</DiskID>
                    <WillWipeDisk>true</WillWipeDisk>
                </Disk>
            </DiskConfiguration>
            <ImageInstall>
                <OSImage>
                    <InstallTo>
                        <DiskID>0</DiskID>
                        <PartitionID>3</PartitionID>
                    </InstallTo>
                    <InstallFrom>
                        <MetaData wcm:action="add">
                            <Key>/IMAGE/INDEX</Key>
                            <Value>2</Value>
                        </MetaData>
                    </InstallFrom>
                    <WillShowUI>OnError</WillShowUI>
                    <InstallToAvailablePartition>false</InstallToAvailablePartition>
                </OSImage>
            </ImageInstall>
        </component>
        <component name="Microsoft-Windows-International-Core-WinPE" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <SetupUILanguage>
                <UILanguage>en-US</UILanguage>
            </SetupUILanguage>
            <InputLocale>0409:00000409</InputLocale>
            <SystemLocale>en-US</SystemLocale>
            <UILanguage>en-US</UILanguage>
            <UILanguageFallback>en-US</UILanguageFallback>
            <UserLocale>en-US</UserLocale>
 </component>
    </settings>
    <settings pass="oobeSystem">
        <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <UserAccounts>
                <AdministratorPassword>
                    <Value>($PASS)</Value>
                </AdministratorPassword>
            </UserAccounts>
            <OOBE>
                <HideEULAPage>true</HideEULAPage>
            </OOBE>
        </component>
        <component name="Microsoft-Windows-International-Core" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
            <InputLocale>en-US</InputLocale>
            <SystemLocale>en-US</SystemLocale>
            <UILanguage>en-US</UILanguage>
            <UserLocale>en-US</UserLocale>
        </component>
    </settings>
    <settings pass="offlineServicing">
    </settings>
<settings pass="specialize">
    <component name="Microsoft-Windows-TCPIP" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <Interfaces>
            <Interface wcm:action="add">
                <Ipv4Settings>
                    <DhcpEnabled>false</DhcpEnabled>
                    <RouterDiscoveryEnabled>false</RouterDiscoveryEnabled>
                </Ipv4Settings>
                <Ipv6Settings>
                    <DhcpEnabled>false</DhcpEnabled>
                    <RouterDiscoveryEnabled>false</RouterDiscoveryEnabled>
                </Ipv6Settings>
                <Identifier>($WIN_MAC)</Identifier>
                <UnicastIpAddresses>
                    <IpAddress wcm:action="add" wcm:keyValue="1">($IP)/($NETMASK_SHORT)</IpAddress>
                    <IpAddress wcm:action="add" wcm:keyValue="2">($IPv6)/($NETMASKv6)</IpAddress>
                </UnicastIpAddresses>
                <Routes>
                    <Route wcm:action="add">
                        <Identifier>1</Identifier>
                        <Metric>10</Metric>
                        <NextHopAddress>($GATEWAYv4)</NextHopAddress>
                        <Prefix>0.0.0.0/0</Prefix>
                    </Route>
                    <Route wcm:action="add">
                        <Identifier>2</Identifier>
                        <Metric>10</Metric>
                        <NextHopAddress>($GATEWAYv6)</NextHopAddress>
                        <Prefix>::/0</Prefix>
                    </Route>
                </Routes>
            </Interface>
            <!--Interface wcm:action="add">
                <Ipv4Settings>
                    <DhcpEnabled>false</DhcpEnabled>
                    <RouterDiscoveryEnabled>false</RouterDiscoveryEnabled>
                </Ipv4Settings>
                <Ipv6Settings>
                    <DhcpEnabled>false</DhcpEnabled>
                    <RouterDiscoveryEnabled>false</RouterDiscoveryEnabled>
                </Ipv6Settings>
                <Identifier>Ethernet 2</Identifier>
            </Interface-->
        </Interfaces>
    </component>
    <component name="Microsoft-Windows-DNS-Client" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <Interfaces>
            <Interface wcm:action="add">
                <Identifier>($WIN_MAC)</Identifier>
                <DNSDomain>($HOSTNAME)</DNSDomain>
                <DNSServerSearchOrder>
                    <IpAddress wcm:action="add" wcm:keyValue="1">($NAMESERVER)</IpAddress>
                </DNSServerSearchOrder>
                <DisableDynamicUpdate>false</DisableDynamicUpdate>
                <EnableAdapterDomainNameRegistration>false</EnableAdapterDomainNameRegistration>
            </Interface>
        </Interfaces>
    </component>
    <component name="Microsoft-Windows-TerminalServices-LocalSessionManager" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
          <fDenyTSConnections>false</fDenyTSConnections>
      </component>
      <component name="Microsoft-Windows-TerminalServices-RDP-WinStationExtensions" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
          <UserAuthentication>0</UserAuthentication>
      </component>
    <component name="Networking-MPSSVC-Svc" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <FirewallGroups>
            <FirewallGroup wcm:action="add" wcm:keyValue="RemoteDesktop">
                <Active>true</Active>
                <Group>@FirewallAPI.dll,-28752</Group>
                <Profile>all</Profile>
            </FirewallGroup>
        </FirewallGroups>
    </component>
    <component name="Microsoft-Windows-Deployment" processorArchitecture="amd64" publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS" xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <RunSynchronous>
           <RunSynchronousCommand wcm:action="add">
              <Description>Delete route</Description>
              <Order>1</Order>
              <Path>cmd /c echo IF "($IPv4)"=="" (route delete 0.0.0.0/0) ELSE IF "($IPv6)"=="" (route delete ::/0) >> %windir%\setup\scripts\SetupComplete.cmd</Path>
              <WillReboot>OnRequest</WillReboot>
           </RunSynchronousCommand>
           <RunSynchronousCommand wcm:action="add">
                      <Description>Change EFI boot order</Description>
              <Order>2</Order>
                      <Path>cmd /c echo cmd /c %windir%\setup\scripts\boot_order_changer.exe setfirstid __EFIBOOT__  >> %windir%\setup\scripts\SetupComplete.cmd</Path>
              <WillReboot>OnRequest</WillReboot>
           </RunSynchronousCommand>
           <RunSynchronousCommand wcm:action="add">
                       <Description>Disable Network Discovery</Description>
                       <Order>3</Order>
                       <Path>cmd /c echo cmd /c %windir%\System32\WindowsPowerShell\v1.0\powershell.exe -command "&amp; {&amp;'Disable-NetFirewallRule' –DisplayName '@FirewallAPI.dll,-32752'}" >> %windir%\setup\scripts\SetupComplete.cmd</Path>
                       <WillReboot>OnRequest</WillReboot>
           </RunSynchronousCommand>
                   <RunSynchronousCommand wcm:action="add">
                       <Description>Setting multiple nameservers</Description>
                       <Order>4</Order>
                       <Path>cmd /c echo cmd /c %windir%\setup\scripts\nameservers.cmd ($NAMESERVERS) >> %windir%\setup\scripts\SetupComplete.cmd</Path>
                       <WillReboot>OnRequest</WillReboot>
                   </RunSynchronousCommand>
           <RunSynchronousCommand wcm:action="add">
                       <Description>Enable language</Description>
                       <Order>5</Order>
                       <Path>cmd /c %windir%\System32\WindowsPowerShell\v1.0\powershell.exe %windir%\setup\scripts\lang.ps1 "($LANG)" >>%windir%\setup\scripts\lang.log 2>&amp;1</Path>
                       <WillReboot>Never</WillReboot>
           </RunSynchronousCommand>
                   <RunSynchronousCommand wcm:action="add">
                       <Description>finish</Description>
                       <Order>6</Order>
                       <Path>cmd /c echo cmd /c %windir%\setup\scripts\wget.exe -O- "($FINISH)" >> %windir%\setup\scripts\SetupComplete.cmd</Path>
                       <WillReboot>OnRequest</WillReboot>
                   </RunSynchronousCommand>
           <RunSynchronousCommand wcm:action="add">
                       <Description>Postinstall script listener</Description>
                       <Order>7</Order>
                       <Path>cmd /c IF "($HAS_RECIPE)"=="1" (echo cmd /c %windir%\System32\WindowsPowerShell\v1.0\powershell.exe  %windir%\setup\scripts\winrm_listener.ps1 "($HOSTNAME)" "($LOCATIONIP)") >> %windir%\setup\scripts\SetupComplete.cmd</Path>
                       <WillReboot>OnRequest</WillReboot>
                   </RunSynchronousCommand>
                   <RunSynchronousCommand wcm:action="add">
                       <Description>Change EFI boot order 2</Description>
                       <Order>8</Order>
                       <Path>cmd /c %windir%\setup\scripts\boot_order_changer.exe setfirstid __EFIBOOT__ >> %windir%\setup\scripts\efi.log</Path>
                       <WillReboot>OnRequest</WillReboot>
                   </RunSynchronousCommand>
                   <RunSynchronousCommand wcm:action="add">
                       <Description>Put key</Description>
                       <Order>9</Order>
                       <Path>cmd /c echo ($PRODUCTKEY) >> %windir%\setup\key</Path>
                       <WillReboot>OnRequest</WillReboot>
                   </RunSynchronousCommand>
                   <RunSynchronousCommand wcm:action="add">
                       <Description>Activating windows</Description>
                       <Order>10</Order>
                       <Path>cmd /c echo cmd /c %windir%\setup\scripts\activate.cmd >> %windir%\setup\scripts\SetupComplete.cmd</Path>
                       <WillReboot>OnRequest</WillReboot>
                   </RunSynchronousCommand>
                   <RunSynchronousCommand wcm:action="add">
                       <Description>TZ setup</Description>
                       <Order>11</Order>
                       <Path>cmd /c IF NOT "($TIMEZONE)" == "()" ( IF NOT "($TIMEZONE)" == "" (tzutil /s "($TIMEZONE)"))</Path>
                       <WillReboot>OnRequest</WillReboot>
                   </RunSynchronousCommand>
        </RunSynchronous>
    </component>
</settings>
    <cpi:offlineImage cpi:source="catalog:c:/distr/7601.17514.101119-1850_x64fre_server_eval_en-us-grmsxeval_en_dvd/sources/install_windows server 2008 r2 serverstandard.clg" xmlns:cpi="urn:schemas-microsoft-com:cpi" />
</unattend>

网上查得,在 xml 文件中,& 的符号要转义。尝试使用 CData 来表达 URL 的变量 ($FINISH) 。但实践中,使用了 CData 后,网址末尾多出来了个分号 ; 符号。无奈,最后将 URL 的变量拆成两部分,前边一部分写固定,后边用变量 ($AUTH_ID) 的组合。

参考:

iredmail - 注册页 - register - 简单制作

需求:有一个简单的 web 表单,让未登录的访客 注册 邮箱
思路:iredadmin 里,有 Create 的类,照它样子,复制出一个 Register 类来;同样,模仿着添加好 web 访问的路径 和 html 表单

环境:debian 12 里安装好的 iredmail - 1.7.1 SQL 版本

请注意,我不会 Python ,这是我在 Poe GPT 帮助下,简单制作的,仅用于测试。

步骤:

文件内容:iRedAdmin-2.6/controllers/sql/urls.py 中,添加 访问路径 :

'/register', 'controllers.sql.basic.Register',

文件内容:iRedAdmin-2.6/controllers/sql/basic.py 中,添加 Register 的类 :

from libs import iredpwd
from libs.sqllib import general as sql_lib_general
class Register:
    def GET(self):
        available_domains = ['iredmail.demo.anqun.org', 'imap.demo.2xinxian.top', 'example.net']  # 示例域名列表
        form = web.input()
        return web.render(
            'register.html',
            available_domains=available_domains,
            msg=form.get('msg'),
        )

    def POST(self):
        form = web.input()

        domain = form.get('domainName', '').strip().lower()
        username = form.get('username', '').strip().lower()
        newpw = form.get('newpw', '').strip()
        confirmpw = form.get('confirmpw', '').strip()
        cn = form.get('cn', '').strip()  # 获取可选的显示名称

        if not username or not newpw or not confirmpw or not domain:
            return web.seeother(f'/register?msg=INVALID_INPUT')

        if newpw != confirmpw:
            return web.seeother(f'/register?msg=PASSWORDS_DO_NOT_MATCH')

        # 生成密码哈希
        pwscheme = 'SSHA512'
        passwd_hash = iredpwd.generate_password_hash(newpw, pwscheme=pwscheme)

        result = self.add_user(domain, username, passwd_hash, cn)  # 将 cn 传递给 add_user

        if result[0]:
            return web.seeother(f'/register?msg=CREATED')
        else:
            return web.seeother(f'/register?msg={web.urlquote(result[1])}')

    def add_user(self, domain, username, password, cn):
        mail = f"{username}@{domain}"

        if sql_lib_general.is_email_exists(mail):
            return (False, 'ALREADY_EXISTS')

        record = {
            'domain': domain,
            'username': mail,
            'password': password,
            'active': 1,
            'name': cn
        }

        try:
            _wrap = SQLWrap()
            conn = _wrap.conn
            conn.insert('mailbox', **record)
            return (True, )
        except Exception as e:
            return (False, repr(e))

创建模板文件 iRedAdmin-2.6/templates/default/register.html

{% extends "layout.html" %}

{% block title %}{{ _('Add mail user') }}{% endblock title %}
{% block navlinks_create %}class="active"{% endblock %}

{% block main %}
{# Show system message #}
{% if msg %}
    {% if msg.startswith('PW_') %}
        {% set _pw_errors = msg.split(',') %}
        {% for _err in _pw_errors %}
            {{ user_msg_handler(_err) }}
        {% endfor %}
    {% else %}
    {#        {{ user_msg_handler(msg) }} #}
        <p>{{ msg }}</p>
    {% endif %}
{% endif %}

    <div class="content-box">
        <div class="box-body">
            <div class="box-header clear">
                <ul class="tabs clear">
                    <li class="active"><a href="#user_add"><i class="fa fa-plus"></i> {{ _('User') }}</a>
                </ul>

                <h2>{{ _('Add mail user') }}</h2>
            </div>

            <div id="user_add" class="box-wrap clear">
                <form name="form_add_user" method="post" action="{{ctx.homepath}}/register">
                    <div class="form-field clear">
                        <h4 class="size-250 fl-space">{{ _('Mail Domain') }} <span class="required">*</span></h4>
                        <span class="clean-padding">
                            <select name="domainName" id="domainSelect" required>
                                {% for domain in available_domains %}
                                    <option value="{{ domain }}">{{ domain }}</option>
                                {% endfor %}
                            </select>
                        </span>
                    </div>
                
                    <div class="form-field clear">
                        <h4 class="size-250 fl-space">{{ _('Mail Address') }} <span class="required">*</span></h4>
                        <span class="clean-padding">
                            <input type="text" size="35" name="username" value="" autocomplete="off" class="text fl-space" required />@
                        </span>
                    </div>
                
                    <div class="form-field clear">
                        <h4 class="size-250 fl-space">{{ _('Password') }} <span class="required">*</span></h4>
                        <span class="clean-padding">
                            <input type="password" name="newpw" required class="text fl-space" />
                        </span>
                    </div>
                    
                    <div class="form-field clear">
                        <h4 class="size-250 fl-space">{{ _('Confirm Password') }} <span class="required">*</span></h4>
                        <span class="clean-padding">
                            <input type="password" name="confirmpw" required class="text fl-space" />
                        </span>
                    </div>

                    <div class="form-field clear">
                        <h4 class="size-250 fl-space">{{ _('Display Name (Optional)') }}</h4>
                        <span class="clean-padding">
                            <input type="text" name="cn" value="" autocomplete="off" class="text fl-space" />
                        </span>
                    </div>                    
                
                    <div class="form-field clear">
                        <span>
                            <input type="submit" name="submit_add_user" value="{{ _('Add') }}" class="button green"/>
                        </span>
                    </div>
                </form>
            </div>{# -- End box-wrap -- #}
        </div>{# -- End content-box -- #}
    </div>{# -- End box-body -- #}
{% endblock main %}

iredmail 自定义注册页 表单

iredmail 自助注册邮箱成功

iredmail 自助注册的邮箱,成功登录到 Webmail

参考: